3.3. Generators

In this section we will reveal as much details about message generators as possible. First we describe the architecture and then we discuss an approach to develop a new generator.

Generator is the most sensitive and fragile component in the whole PerfCake architecture. It has the reponsibility of generating all the messages and load. It is recommended to study the MessageGenerator interface, AbstractMessageGenerator as its basic implementation and then the default implementation called DefaultMessageGenerator before developing your own generator.

3.3.1. Generators Architecture

Figure 3.2. Generators Architecture

The main responsibility of the generator is creation of SenderTask instances whil controlling the number of threads used and the speed of creation of these tasks. A generator also keeps a thread pool that executes the tasks. The tasks are then processed as fast as possible. There is nothing a generator could or should do about the speed of the SenderTask execution.

A message generator is the most crucial and complicated component of PerfCake and it is highly recommended to reuse one of existing implementations as they already offer mostly wanted features.

The message generator needs to take care of the sending threads, create SenderTasks as needed and monitor the test progress. It is important to properly shutdown the message generation for both time and iteration based test length control. In the case of an iteration based control, a generator must wait for all the tasks to be processed (in case of a normal/unexceptional termination). In the case of a time based control, the test stops immediately after the time has elapsed.

Each SenderTask takes a reference to its parent generator to notify this generator of any errors that might have occurred.

A message generator carries links to all other system components and thus has an ultimate control over the running performance test. It is the only class that indirectly manipulates RunInfo through starting and stopping the ReportManager.

A message generator usually maintains a queue of prepared SenderTasks and schedules their execution. It also controls the number of parallel threads running.

3.3.2. Writing a New Generator

The best way to implement a new generator is by modifying DefaultMessageGenerator. The main method is generate() that creates a new thread pool and generates SenderTasks until the test is finished. SenderTasks are submitted as tasks to the newly created thread pool. In the end the thread pool is shut down according to RunInfo configuration as described in the previous section.

It is worth noticing the custom thread factory DefaultMessageGenerator.DaemonThreadFactory. This makes sure we can quit the process even if some threads got stuck. It also sets higher priority to these threads.

DefaultMessageGenerator keeps an eye on the number of prepared SenderTasks by using a BlockingQueue that is passed to the underlying thread pool executor. This limits the size of memory used at once.

Now investigate the AbstractMessageGenerator.newSenderTask() method to see how SenderTasks are created. You can notice that SenderTasks constructors takes the generator to be able to report failures back. Other test execution related classes are set on the SenderTask as well for it to be able to complete its goal. A SenderTask instance implements Runnable and except for default Java API, there is no notification of its completion.

In the end just have a look to the SenderTask class. This class implements Runnable and does the following steps. First, it acquires a MessageSender from the senders pool, uses this MessageSender to send one or more messages while measuring the time it took. When a Receiver is used, it awaits for a message response on a separate message channel. Upon the response's arrival, the total time is measured. In the end, the MessageSender is released. When a Receiver is in place, the MessageSender is not returned to the pool until we get a response corresponding to the original request. This prevents another thread from using the same MessageSender and thus increasing the number of concurrent clients.