Performance and System Environment
We have been building Internet applications for quite a few years, and it is our experience that one of the most common problems is that after it is installed, the solution is always too slow. This is something that all applications suffer from, as you can see in the various discussion forums of any software product.
This does not always mean that the programming is sloppy (although perhaps often it is). There is often a great difference in the speed an application can actually achieve and the perceived performance that the end-user might experience. Also, a system that performs well when only one user accesses it might collapse if several users send requests at the same time.
A system that integrates many different data sources might suffer from bad performance even though the actual portal application might be fast enough. A portal's speed is defined to a great extent by the speed at which data from external systems is delivered. So a portal will be slow if one of the data sources is slow. Unfortunately, no one will care that it is not your fault if it takes minutes for the portal to appear in the browser.
When designing a complex software solution, it is always best to define performance expectations beforehand and to test for performance as early as possible. This sounds simple, but this point is often forgotten until it is too late. If nobody takes the time at the beginning of the project to define the expected performance, the system will always be too slow. It is a lot more difficult to correct performance problems after the solution is in a production environment.
When we installed our first online Internet banking solution, very few people accessed their accounts via the Internet. The application worked well and delivered the web pages quickly enough. However, no real stress testing was performed at the beginning, so we did not really know how many requests our system could handle. Over the months the application was installed, the number of requests grew slowly but steadily. Still, no stress testing was done. After all, the system ran OK didn't it? Then, for some strange reason, the number of people using the system suddenly exploded overnight! Needless to say, the whole system collapsed under the load. It was far worse having a nonfunctional system in this situation than it would have been when Internet banking was still an exotic application.
How do you define a system's expected performance? It depends on what the system is supposed to do. The first thing you can do is check out the data sources and decide what sort of performance you can expect from them. If you are integrating standard data sources (such as a standard database), you can often obtain performance data from the vendor. Get that data, but take in the information with a grain of salt. To really check, you need to run your own isolated tests against the single system if you can. It is much more difficult to find bottlenecks after everything is integrated.
Before you start evaluating the performance of individual systems, make sure you also define your computing environment. What's the good of testing the system on some high-powered system if it will actually be running on a low-end box? Also make sure you test on the same operating system and using the same hosting software (such as a servlet engine). The servlet API might be standardized, but in reality you will find that life is not so simple. And it is a lame excuse to say, "We didn't test on that system" when a complaint comes in.
Another way to find out what to expect from your system is to check out other solutions that might do the same thing you are planning on doing. See how fast they run, and try to obtain some information on how they work. Check out case studies, often published on web sites, to find out the architecture used to build the application. You might also be able to find out by asking whoever built the system.
As soon as you are satisfied that you know what to expect of your application, here are some tips on what you can use in Cocoon to achieve the fastest possible application:
Use the built-in Cocoon caching whenever possible when building your pipelines.
If you need to write your own components, make sure they support the caching interfaces in Cocoon if possible.
Stress-test your application using an available tool, and observe how the performance changes if you adjust the pooling of Cocoon components.
Make sure you are running your application with the lowest level of trace, where only errors are logged.
Another piece of advice when writing components that connect to a specific data source (especially if it is not your data source) is to make sure you add a time trace. In other words, trace when you connect to the external data source, and trace when the data is returned. That is the time someone else has to worry about.
If, after testing with stress tools, you find that your system performance is not good enough, you will want to look into what else you can do to improve the response time. Obviously it is a good idea to make sure the system has enough memory and the processor is fast enough. If you are running in a servlet environment, you might want to try an alternative servlet engine to see if you can get better performance.
You might also want to look into front-side and back-side caching. A front-side cache is placed between Cocoon and the Internet. Any client program requesting a particular document receives it from the cache, not from Cocoon itself. The cache can store the complete document and request it from Cocoon only if it has expired. Cocoon then generates the new document and serves it to the cache to be stored. Look into how you can control the expiration of generated documents using the appropriate HTTP headers in your documents. There are several ways of doing this. For example, the Cocoon reader component allows you to set HTTP headers. Another way is to write your own component, such as an action that sets headers when used in a pipeline.
If you are accessing an external data source that is too slow, you might need to implement a backside cache. This type of cache sits between Cocoon and the external data source. The pipeline requests the data from the cache, not from the data source itself. There are various ways of implementing the cache. You can look at the description of how Cocoon caches pipelines to get some ideas on how to implement your own.
It is a good idea to provide the user with some visual feedback to show what is going on. If the user cannot see anything happening on the screen, he will perceive system performance as being too slow, even though it might not be. One way of doing this is to load an intermediate page that says something like "Please wait; your data is being fetched" and then let this page call the function on the server that does this. Presenting the user with something to read while the work goes on in the background means that by the time the user has finished reading, part or all of the data will have been retrieved. Look into redirects and metatags to do this if you are building a site in HTML.
When designing HTML web sites, one of the mechanisms used most often is frames. Although this is not a book on magical HTML design, here's a piece of advice: Remember that each part of a frame causes a new request to be sent to the server. So if you have a page containing four different parts (header, footer, navigation, and actual content), that is a total of five requests to the server and five pipeline calls in Cocoon. Try to reduce the use of frames if possible. One way is by using Cocoon's content aggregation to aggregate the different parts of a page and then use a stylesheet to format the output.
In addition to the tips just discussed, there are additional areas you will want to check when you design the output formatwhich brings us to presentation.