Tuesday, 21 June 2011

I/O Benchmarks with Bonnie++

So, after my last article using the palimsest application, I decided to give a tool called Bonnie++ a try. From the description on the authors' website:

"Bonnie++ is a benchmark suite that is aimed at performing a number of simple tests of hard drive and file system performance. Then you can decide which test is important and decide how to compare different systems after running it. I have no plans to ever have it produce a single number, because I don't think that a single number can be useful when comparing such things."

So, after running the default program as provided by Ubuntu repositories, we get the following output:

$ bonnie++
Writing a byte at a time...done
Writing intelligently...done
Rewriting...done
Reading a byte at a time...done
Reading intelligently...done
start 'em...done...done...done...done...done...
Create files in sequential order...done.
Stat files in sequential order...done.
Delete files in sequential order...done.
Create files in random order...done.
Stat files in random order...done.
Delete files in random order...done.
Version 1.96 ------Sequential Output------ --Sequential Input- --Random-
Concurrency 1 -Per Chr- --Block-- -Rewrite- -Per Chr- --Block-- --Seeks--
Machine Size K/sec %CP K/sec %CP K/sec %CP K/sec %CP K/sec %CP /sec %CP
srdan-desktop 8G 505 98 83083 10 36609 4 2514 86 96195 5 155.8 3
Latency 16674us 1003ms 1830ms 41497us 269ms 1014ms
Version 1.96 ------Sequential Create------ --------Random Create--------
srdan-desktop -Create-- --Read--- -Delete-- -Create-- --Read--- -Delete--
files /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP /sec %CP
16 11634 22 +++++ +++ 17458 25 16959 30 +++++ +++ 17864 23
Latency 55341us 601us 638us 753us 43us 123us
1.96,1.96,srdan-desktop,1,1308644057,8G,,505,98,83083,10,36609,4,2514,86,96195,5,155.8,3,16,,,,,11634,22,+++++,+++,17458,25,16959,30,+++++,+++,17864,23,16674us,1003ms,1830ms,41497us,269ms,1014ms,55341us,601us,638us,753us,43us,123us


So, overall we have a lot more detail, although not as many pretty graphs as we had with Palimpsest. Also, I'm afraid my blogger skills have let me down, because the output above actually looks a lot better from the command line, but I can't for the life of me figure out how to retain tabs when posting.

Looking at the output we can see that the tool tests five main categories:

* Sequential Output (Per Chr, Block, Rewrite)
* Sequential Input (Per Chr, Block)
* Random Seeks
* Sequential Create (Create, Read, Delete)
* Random Create (Create, Read, Delete)

According to the documentation the first three categories simulate the same kind of I/O load that a database would normally put onto a disk, while the last two categories simulate the kind of load that you would expect to see in an NNTP and/or web caching server. From what I could tell, the first three tests all create a single file to test on and the last two create a host of small files.

One thing to note is that this is a test of both the disk and the filsystem (and kernel), unlike the palimpsest benchmark, which only tests the disk. Another thing to note is that as well as getting the I/O throughput we're also seeing the %CP figure, i.e. how taxing the operation is on the CPU. This might be an important factor, when trying to determine what kind of CPU you need for your web caching server.

Overall, bonnie++ is a very good tool for looking at the holistic performance of any storage system.

Saturday, 11 June 2011

I/O Benchmarks with GNOME Disk Utility

Recently I decided to tidy up my home office. In the process I found a lot of old computer hardware which has built up over the years. One of the more interesting things I found was several old (ATA) hard drives. This got me thinking how I could make use of these drives. The first thing that popped into mind was using them for benchmarking purposes. i.e. to get familiar with the tools used to benchmark I/O. Being a Ubuntu user, I noticed that there's a really nice utility installed by default (go to 'System'->'Administration'->'Disk Utility'). This is actually a program called Palimpsest/GNOME Disk Utility and as well as having the ability to benchmark hard drives it also gives you the ability to use the disks' SMART capabilities to get some idea of the number of bad sectors as well as other warning signs that the drive may be failing.

So, I've shut down my computer and plugged in the drives (no hot plugging with ATA unfortunately), booted up and ran the 'Read-Only' benchmark. This gives some basic numbers showing the maximum, minimum and average read times. When I went to do the 'Read/Write' benchmark, I found that you had to completely format the disk in order to benchmark it. This involved not only deleting all of the partitions, but also deleting the MBR. Once this was done, I was able to run the Read/Write tests on both of the 40GB drives. As well as the max, min and avg times, you also get a pretty graph:

The red line corresponds to writes, with the blue line corresponding to reads. I'm not sure what all of the green points and lines correspond to. So, how did the drives perform? One of them (the one pictured above) had quite a bit of variance as you can see in the graph above. Also, the read/write rates crossed at about 40% of the way through the test, which I don't quite understand. As a comparison, the other 40GB drive I tested was amazingly stable, with the minimum read rate only 1.2 MB/s below the maximum and the write max/min only differing by 0.1 MB/s. The output of this drive can be seen below:


So, now the only question left is "How will they perform in a RAID array?". After putting the two drives into a RAID-0 array (note that you need to install the mdadm package first), we see the following results:


The results are quite interesting in that they show that the avg read rate for the array was actually worse (21.5 MB/s) than either of the two individual drives (24.6 MB/s and 22.3 MB/s). The avg write rate was noticeably higher at 26 MB/s, compared to the individual results of 22.1 and 22.9 MB/s.

So what can we conclude from this? We probably shouldn't look too much into the results as both of the hard drives are old and from different manufacturers. However, in saying that, I think it does show that write intensive applications might benefit from RAID0 more than read intensive ones.

Tuesday, 3 May 2011

Installing APR based Tomcat Native library

After installing Tomcat I kept seeing the following message in the Catalina logs:

INFO: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: /usr/lib/jvm/java-6-openjdk/jre/lib/i386/client:/usr/lib/jvm/java-6-openjdk/jre/lib/i386:/usr/lib/jvm/java-6-openjdk/jre/../lib/i386:/usr/java/packages/lib/i386:/usr/lib/jni:/lib:/usr/lib

This got my curiosity up and after learning about APR and the benefits it supposedly offers, I decided I would try it out and see if I could notice any difference in performance. Luckily, there's a package in Ubuntu for the Tomcat native libraries, making it easy to install:

sudo apt-get install libtcnative-1

This should install the library as well as all of the packages that it depends on. I restarted Tomcat after installing this package, but it still didn't detect the library. I couldn't get around this problem and didn't manage to solve it. It turned out that the next time I started my laptop I didn't get the warning in the logs, but instead I saw:

03/05/2011 3:39:15 PM org.apache.catalina.core.AprLifecycleListener init
INFO: Loaded APR based Apache Tomcat Native library 1.1.19.
03/05/2011 3:39:15 PM org.apache.catalina.core.AprLifecycleListener init
INFO: APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].


I think that some part of the Tomcat/JVM stack scans for the libraries when the computer starts and caches them from then on and this was what was causing Tomcat to not see the native library files straight away after install.

Now that Tomcat had detected and was using the libraries, I loaded up a few test projects as well as looking at the example projects included with the Tomcat install, but couldn't notice any difference in performance. Hopefully in the next few weeks I can go ahead and test out Tomcat performance using JMeter, ab or something similar and compare the results with APR and without.

NOTE: Just as an update to this post, the process for installing Tomcat Native on CentOS is a lot more complicated as there is no 'libtcnative' package for CentOS and the best guide I've found for it so far involves compiling 'libtcnative' from source.

Tuesday, 26 April 2011

VirtualBox - Increase screen resolution of guest

In order to get a decent resolution in your VirtualBox (Ubuntu) guest, you need to install the virtualbox guest X11 package:

sudo apt-get install virtualbox-ose-guest-x11

Grails part 3 - database and deployment

In this, part 3 of the series on Grails, I am going to talk about how to configure our web application to use the MySQL database as our default permanent store and then how to create a war file and deploy to Tomcat.

Firstly, we're going to install MySQL. Luckily the state of Linux package management has advanced to the point where this is as simple as:

sudo apt-get install mysql-server

It should ask what you want to set the root user password to and then install the database. Once it has installed, the database server should be started. To check this, you can run the ps aux | grep mysql command. You should see a line in the output with:

mysql 1004 0.0 0.6 178908 27960 ? Ssl 21:50 0:00 /usr/sbin/mysqld

Then we need to create the database:

CREATE DATABASE Bookstore_dev;

Now that we have our database ready to go, we need to get the MySQL driver so that our grails application can connect to the database. Go to http://www.mysql.com/downloads/ and download the Connector/J package from the MySQL connectors section. Uncompress the package and copy and paste the mysql-connector-java-5.1.15-bin.jar file into the BookStore/lib folder.

Next we'll need to modify our BookStore/grails-app/conf/DataSource.groovy file to specify that we want grails to use the MySQL database instead of the typical HSQLDB that is used. The DataSource.groovy file has three different sections "development", "test" and "production", corresponding to the three different stages of the development process and the three different environments that you can work with in grails. You can define a different database for each stage/environment. There is also a default "dataSource" section at the top, which is used unless the values are overwritten in each of the different sections. To start with, we're going to specify that the development environment should use the MySQL database. We can do this by modifying the development section to look like:

development {
dataSource {
dbCreate = "create"
url = "jdbc:mysql://localhost/Bookstore_dev"
driverClassName = "com.mysql.jdbc.Driver"
username = "root"
password = "password"
}
}


Ofcourse you'll need to change the username and password to whatever you've set them to. I'll also point out that it's not good practice to use the root user to access our database, because if our application gets hacked, our whole database would be compromised. It would be best from a security standpoint to create a new user with privileges limited to the "Bookstore_dev" database. However, since this is just our development database and we're only making it available to our local computer network for the time being it should be ok.

If we now start up our application using the grails run-app command, and once it's started browse to http://localhost:8080/BookStore, we should be able to see our application. We can then add some dummy data to check that it's getting saved to the database. I've gone ahead and added the authors "Stephen King" and "Robert A. Heinlein" and the books "Pet Cemetery", "Stranger in a Strange Land", "The Moon is a Harsh Mistress" (associating them with their respective authors). If you log into the database and have a look at it's contents you can see that the values have been added:

mysql> USE Bookstore_dev;
mysql> SHOW TABLES;
+-------------------------+
| Tables_in_Bookstore_dev |
+-------------------------+
| author |
| book |
+-------------------------+
2 rows in set (0.01 sec)

mysql> SELECT * FROM author;
+----+---------+--------------------+
| id | version | name |
+----+---------+--------------------+
| 1 | 0 | Stephen King |
| 2 | 0 | Robert A. Heinlein |
+----+---------+--------------------+
2 rows in set (0.01 sec)

mysql> SELECT * FROM book;
+----+---------+-----------+----------------------------+
| id | version | author_id | title |
+----+---------+-----------+----------------------------+
| 1 | 0 | 1 | Pet Cemetery |
| 3 | 0 | 2 | Starship Troopers |
| 4 | 0 | 2 | Stranger in a Strange Land |
+----+---------+-----------+----------------------------+
3 rows in set (0.00 sec)

So, we can see that the data is being saved to the database and that the association between the Author and Book object is represented with the 'author_id' field in the Book table. It's also worth noting the "version" field which is updated by grails every time any of the fields in the row are modified.

So now that we've got an application which uses a database it's time to deploy it to our *production* server. We're going to modify our DataSource.groovy file to ensure that the production environment (the one we're going to deploy) also uses the MySQL database:

production {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost/Bookstore_dev"
driverClassName = "com.mysql.jdbc.Driver"
username = "root"
password = "password"
}
}


Make sure you've executed the run-app command with dbCreate set to "create" before deploying this production code as dbCreate = "update" expects the tables to already be created in the database.

Now we can create the war file which we're going to upload to Tomcat through the manager web-app by running the command grails prod war. This generates a production environment war file. The production environment is optimized for code efficiency, while the development and testing environments are optimized for developer productivity. Once the command finishes executing we should have our war file under BookStore/target/BookStore-0.1.war. The 0.1 is the application version number and can be changed in the BookStore/application.properties file.

Now we can log into our Tomcat manager application (found at http://localhost:8080/manager/html if you've setup Tomcat according to the previous post), go to the Deploy section, select our WAR file and hit 'Deploy'. Once the page refreshes we should see our BookStore app in the list of applications and the column "Running" should be set to "true". We can now click on the link in the Path column to go to our web-app and start using it.

As an alternative way to deploy your application, you can also make use of the tomcat grails plugin. In order to do this you need to add a few variables to the BookStore/grails-app/conf/Config.groovy file, namely:

tomcat.deploy.username = "[tomcat manager user]"
tomcat.deploy.password = "[tomcat manager password]"
tomcat.deploy.url = "http://localhost:8080/manager"


Deploying the application is now achieved from the command line with:

grails prod tomcat deploy

This code essentially does the same thing that we did, makes a war file and deploys it, but might be preferable as it is only one step instead of two.

So there you have it, we've taken our simple web application, configured it to use a permanent datastore and deployed it to our Tomcat webserver.

NOTE: If you need to update your deployed application, the way to do it is to first "undeploy" the application from Tomcat, which can be done with "grails prod tomcat undeploy"

Wednesday, 20 April 2011

Tomcat6 on Ubuntu

In this post I'm going to talk about getting Tomcat version 6 up and running on Ubuntu. I'm going to be using Ubuntu 10.10. Installing Tomcat is quite simple, due to it being in the repositories. Simply run:

sudo apt-get install tomcat6 tomcat6-admin tomcat6-examples tomcat6-docs

And that's it :) You now have a working install of Tomcat version 6 on your machine. In order to see it working point your browser to http://localhost:8080/. You'll see the Apache "It Works!" message as well as a message about where everything is and links to the docs, examples, manager and host-manager applications.

Now, in order to get access to the manager and host-manager applications you're going to have to add a user. To do this, modify the file at /etc/tomcat6/tomcat-users.xml and add the following section:

<role rolename="admin"/>
<role rolename="manager"/>
<user username="srdan" password="password" roles="admin,manager"/>


After editing the file and restarting the Tomcat server we should be able to log into the manager application by pointing our browser at http://localhost:8080/manager/html and we should be able to log into the host-manager by going to http://localhost:8080/host-manager/html. The manager will allow us to deploy, undeploy, start and stop our applications and the host-manager allows us to declare, remove, start and stop our virtual hosts.

NOTE: From Tomcat 6.0.3 onwards, there is no "manager" role that is recognised by the manager application, rather it has been split up into four roles for security purposes:

manager-gui - allows access to the HTML GUI and the status pages
manager-script - allows access to the text interface and the status pages
manager-jmx - allows access to the JMX proxy and the status pages
manager-status - allows access to the status pages only

To test out the deployment, we're going to create a very simple web application. First, create a new directory and change into it:

mkdir HelloWorld
cd HelloWorld


Next we're going to create a servlet with the following code:

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {


public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();

out.println("<html>");
out.println("<head>");

out.println("<title>Hello World!</title>");
out.println("</head>");
out.println("<body>");

out.println("<h1>Hello World!</h1>);

out.println("</body>");
out.println("</html>");
}
}

And put it into a file called HelloWorld.java. After this we need to create a WEB-INF and WEB-INF/classes directory. Once we've done this, we compile the above code with:

javac -classpath "/usr/share/tomcat6/lib/*" HelloWorld.java

This should result in a HelloWorld.class file in the same directory. We need to move this file to the WEB-INF/classes directory:

mv HelloWorld.class WEB-INF/classes/

Now we need to add a web.xml file, which is going to tell Tomcat about our application. For our purposes we need to create a file with the following contents:

<?xml version="1.0" encoding="ISO-8859-1"?>

<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">

<description>
Simple Hello World Servlet.
</description>
<display-name>Simple Hello World Servlet</display-name>

<servlet>
<servlet-name>HelloWorld</servlet-name>
<servlet-class>HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>HelloWorld</servlet-name>
<url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
</web-app>


Once that's done, make sure that the file is in the WEB-INF directory, and we can go ahead and create our war file with:

jar -cvf hello.war .

Now we should be able to go back to the manager application at http://localhost:8080/manager/html, go to the Deploy section, select our war file, upload it and have it deploy straight away.

Assuming everything went fine, we can now go to http://localhost:8080/hello/HelloWorld and see our servlet in action, printing out the famous "Hello World!" message in HTML.

That concludes this post. Hopefully now you know how to install Tomcat on Ubuntu, manage it using the manager and host-manager applications, as well as how to create and deploy a very simple web application to the server.

Monday, 18 April 2011

Grails part 2 - First Project

In my last post we talked a little bit about the web framework known as Grails and went through the steps required to get a working Grails development environment setup. In this post we are going to go ahead and create our first working application using the framework.

For the first application, we're going to be creating a very simple application called BookStore. It's going to consist of only two types of "objects", called "Book" and "Author". So, to begin with, the first command we want to run is:

grails create-app BookStore

This will go ahead and create the project as well as producing a lot of output. The output is the result of Grails creating the default project directory structure. Being a COC framework, grails assumes certain things about how we're going to layout our project, meaning that the unit tests are under BookStore/test/unit, the integration tests are under BookStore/test/integration and the CSS files are under BookStore/web-app/css.

Next we need to change into the newly created project directory and create the Book domain class:

cd BookStore
grails create-domain-class bookstore.Book


The "create-domain-class" command has gone and created two new files for our project, one to define our Book class and the other a Unit test class for Book. They can be found under:

BookStore/grails-app/domain/bookstore/Book.groovy
BookStore/grails-app/test/unit/bookstore/Book.groovy


In order to do something a little more useful with our domain class, we're going to have to give it "properties". In order to do this, open the domain class definition for Book and modify it to look like the following:

package bookstore
class Book {

String title
Author author
static constraints = {
}
}


We now have a Book class with a "name" and an "author" property. However, we haven't created the Author class yet, so this property isn't pointing to anything at the moment. Let's change that by creating an Author class:

grails create-domain-class bookstore.Author

This should create the Author domain class in much the same way that it created the Book class. Let's modify this new class to have a name property and let's add a property to point to all of the books that this author has written. Modify the BookStore/grails-app/domain/bookstore/Author.groovy file to look like the following:

package bookstore
class Author {
String name
static hasMany = [books:Book]
static constraints = {
}
}


The line static hasMany = [books:Book] is an important one as it defines the "many" part of the "one-to-many" relationship between Book and Author. i.e. each Book has one Author and an Author can have many Books.

We can now run our application with:

grails run-app

And then navigating to http://localhost:8080/BookStore/. However, at the moment this just shows us the default grails home page and there's not much we can do with it. This is because we haven't defined any controllers in our application. Controllers, of MVC fame, are the things which handle the application logic of our web-app. What does this actually mean? Well, they receive incoming requests, decide where to send them and how to deal with any incoming data. They also supply data to the view layer to be used to display our objects to the user.

In order to grace our application with some controllers and views, we will execute the generate-all command which will get us up and running with both a controller and views:

grails generate-all bookstore.Book

We then also need to do this for the Author class:

grails generate-all bookstore.Author

Now, when we run the grails run-app command and navigate to http://localhost:8080/BookStore we should see two controllers on the default Grails page. Selecting either of these controllers will allow us to execute the full set of CRUD commands on the Book and Author classes.

If you've had a go at creating a new Author and/or Book object(s) you'll notice that when creating a new Book for example there is an Author drop down generated containing a list of all of the Authors. However, instead of displaying the Author's name property, the drop down is listing the id and the class type, which is not very pretty. To change this behaviour we're going to overwrite the toString method for both the Book and Author class.

Modify Book.groovy to look like:

package bookstore
class Book {

String title
Author author
static constraints = {
}
String toString(){ return title }
}


And Author.groovy to look like:

package bookstore
class Author {
String name
static hasMany = [books:Book]
static constraints = {
}
String toString(){ return name }
}


And that's it for now. We've ended up creating a simple BookStore application with two domain objects in a one-to-many relationship.

I've only started scratching the surface of what can be done with Grails. In the coming weeks I'd like to go over unit tests, views, authentication and authorization and as many more topics as time will allow.