Concepts
The following sections will describe the concepts and components used within Basher in more detail.
Tasks
At a high level, the typical Basher setup contains of any number of small task instances, configured by a task configuration. The combination of a task and a task configuration is called a task contribution. The task contribution is provided to the task manager, which manages task contributions at runtime.
Runs and Contexts
A Run a set of threads executing available tasks according the configuration in a context. The context defines how long the different phases of a run should be (phases are explained below), how many threads should be used to execute the tasks, when statistics collection should be done and so forth. Take a look at the contest Javadoc for more information.
Contexts are managed by the context mananger, which handles the adding, removing and marking contexts as active.
Life-cycle States & Phases
These components together form the parts required to perform a Basher 'run', which in Basher consists of 7 different life-cycle states & phases:
- Start: Initial state when system is started
- Setup: Phase in which setup of required data, configuration, etc can be done.
- Warm-up: Initial phase before a run is started. This phase can be used to 'warm-up' the JVM & HotSpot.
- Run: The main phase where 'work' actually happens. This phase is also the phase in which statistics is captured
- Cool-down: Phase allows tasks to complete their work, but statistics is not captured for these tasks.
- Tear-down: Phase used to close any open resources
- End: End state of when all work has been completed.
Tasks can be configured to run in different life-cycles, to allow one task to, for example, create pre-requisite data, while another task uses thi data in the Run
Infrastructure
Internally, Basher uses Gaderian, which allows easy configuration and bootstrapping functionality. It also allows a very complete system to be built up around the running tasks without Basher having necessarily to know about it.
Below follows a summary of the main components within Basher and how to use and interact with them.
Task
A task is the key component to the Basher. It is defined as an interface to be implemented by the application developer and is the link between Basher and the application being tested and provides Basher with the work it is intended to measure. For convenience, Basher has a default implementation of the Task interface available called AbstractTask that provides some helper methods for managing errors and the like during a run.
The only thing a task is required to expose is a method to be executed as part of a run. The default name of this method is executeTask.
Timed Task
A timed task is the 'special' form of task within the Basher system. It is still an instance of the Task interface, but (as the name may indicate) it is executed in a timed (interval) fashion. It allows certain operations to happen at certain points in the performance test run.
The timed task has the same properties as any other task.
Followers
In many usage scenarios, tasks usually consists of a set of consecutive steps (or smaller tasks). Basher provides a way for defining a set of tasks that logically follow their "parent" task. For example, a "create account" task is usually followed by a "login task". Note that the use of 'usually' is important in this context since the task may *not* always run.
The concepts of 'followers' allows for complex usage scenarios to be easily modelled by combining a set of smaller tasks.
Task Configuration
A task configuration specifies the behaviour of the task, the way it will be executed by Basher.
The following key properties (defined as simple java bean properties) are captured by the task configuration:
Property | Description | Default |
---|---|---|
name | The name of the task. This name can be used to retrieve the task from the task manager by it's name | Fully qualified class name |
weight | The chance for the task to be invoked | 100 (always) |
inertia | The amount the weight should be multiplied by for every successful invocation (a float between 0 and 2) | 1.0 (no intertia) |
runFrom | Specified the time (in seconds) before which the task should not execute | 0 (run from start) |
stopAfter | Specifies the time (in seconds) from which point the task should not execute further | Long.MAXVALUE (run till end) |
maxInvocations | Specifies maximum number of times Basher is allowed to invoke the task | 0 (no maximum) |
phases | Specifies the phases in which the task should be executed | RUN |
Basher Context
The context controls all aspects of a run. It defines the number of threads, the length of the various phases, when collection is done, etc.
The following properties are configurable through the BasherContext:
Property | Description | Usage | Default |
---|---|---|---|
name | Name of the context. Used for look-ups of the contexts | Required | No default |
runIdentifier | Identifier to used for the run | Optional | no default |
manuallyControlled | Flag indicating that phase transitions are controlled by Basher (default behaviour) or by the user, using for example BeanShell | Optional | false (phase transitions handled by Basher) |
setupDuration | Defines the length of the setup phase (defined in seconds) | Optional | 10 seconds |
warmupDuration | Defines the length of the warmup phase (defined in seconds) | Optional | 0 seconds |
runDuration | Defines the length of the run phase (defined in seconds) | Optional | 10 seconds |
cooldownDuration | Defines the length of the cooldown phase (defined in seconds) | Optional | 10 seconds |
teardownDuration | Defines the length of the teardown phase (defined in seconds) | Optional | 0 seconds |
startCollectionFrom | Defines delay before statistics collection should begin (in the run phase) | Optional | No default (start collection immediately) |
stopCollectionAfter | Defines delay before statistics collection should stop (in the run phase) | Optional | No default (stop collection at the end of run phase) |
initialNumberThreads | Number of threads which are created at the start of a run | Optional | 10 |
maxNumberThreads | Maximum number of threads that may exist in the system (used normally in combination with thread incrementors) | Optional | 0 (no maximum) |
threadIncrementInterval | Interval between adding more threads to the runtime | Optional | 0 (no incrementing of threads) |
threadIncrementCount | Number of threads to be added for each interval. Only used if threadIncrementInterval is defined | Optional | 0 |
taskMinDelay | Minimum delay between tasks. Mainly used to stagger execution of tasks | Optional | 0 (no delay) |
taskMaxDelay | Maximum delay between tasks. Mainly used to stagger execution of tasks | Optional | 0 (no delay) |
markAverageInterval | Interval at which execution averages are calculated. Defined in seconds | Optional | 10 seconds |
beanShellScriptDirectory | Name of directory containing BeanShell scripts to use during initialization | Optional | src/main/beanshell |
reportDirectory | Name of the directory of where reports should be captured | Optional | target/basher-reports/ |
includes | Comma separated string containing include patterns (net.sourceforge.basher.example.tasks.*,com.acme.tasks.*) | Optional | * (all tasks) |
includes | Comma separated string containing exclude patterns (net.sourceforge.basher.example.tasks.*,com.acme.tasks.*) | Optional | * (all tasks) |
TaskManager
The task manager is responsible for managing tasks within the Basher. All tasks must be registered with the task manager in order to be executed within a given testing scenario. It will, upon request, select a task randomly from the pool of available tasks and return it to the caller.
The selection algorithm is currently entirely random and is simply a random index, based on the size of the pool.
Scheduler
The scheduler is the default (and currently the only) service responsible for managing life-cycles within the system. It creates and starts an initial number of threads that in turn are responsible for executing the tasks registered within the system. It will also maintain state transitions between phases.
By default, the scheduler will start up 10 threads, but this can be overridden by specifying your own value for the 'initialNumberThreads' property in the BasherContext.
If not using the Maven plugin, the scheduler can be started by obtaining a reference to it through the Gaderian registry and execute the start() method.
... Registry registry = ... Scheduler scheduler = (Scheduler)registry.getService(Scheduler.class); scheduler.start(); ... do some waiting while the tasks run scheduler.stop(); // Shuts the scheduler down again ...
Thread Incrementors
In some cases, it may desirable to increment (ramp up) the number of threads that are used during the run. This can either be done using BeanShell (by interacting with the Scheduler) or by using the thread incrementor functionality built into Basher.
By default, this functionality is not active, but can be set to increment the number of threads by X at an interval of N by defining the properties 'threadIncrementCount' and 'threadIncrementInterval' respectively in the BasherContext.
Event Manager
Internally, Basher uses an event driven approach to inform interested parties about life-cycle transitions, task management and various other information.
The event manager API is the main entry point into the events sub-system.
The current set of events available:
- CollectionStartedEvent: Raised when collection is about to be started
- CollectionStoppedEvent: Raised when collection is about to be stopped
- NoTasksAvailableEvent: Raised when the task manager has run out of tasks to select from during a phase
- TasksAvailableEvent: Raised when the task manager again has tasks available to select from during a phase
- ThreadAddedEvent: Raised when a thread has been added
- ThreadRemovedEvent: Raised when a thread has been removed
- TickEvent: Raised when a 'tick' occurs in the system. A tick is the interval between capturing averages
- PhaseTransitionEvent: Raised when a phase transition (setup->run for phase transition for example)
Custom events may be published through the EventManager by extending the BasherEvent class and publishing them using the publish() method of the EventManager.
Note: The event management system if currently single-threaded but this may move to being multi-threaded going forward.
Context Manager
The context manager is responsible for managing (mainly storing) registered Basher contexts at runtime. The contexts can be provided to the context manager by contributing to the net.sourceforge.basher.BasherContexts configuration point & and at runtime, using the add method.
The following is an example of contributing a Basher context using Gaderian:
... <contribution configuration-id="BasherContexts"> <basher-context name="default" setup-duration="10" run-duration="20" cooldown-duration="10" start-collection-from="0" stop-collection-after="0" initial-number-threads="5" max-number-threads="5" thread-increment-count="0" thread-increment-interval="0" mark-average-interval="10" task-min-delay="0" task-max-delay="1" report-directory="target/basher-reports" /> </contribution> ...
Using the getActiveBasherContext method, it is possible to retrieve the currently active BasherContext.
Miscellaneous
Bean Shell
Basher also offers the facility of configuring a set of task (based on the registered tasks) using BeanShell. This allows for an quick and easy way of building up fairly complex scenarios using Basher.
When a phase transition occurs, the BeanShell initializer will invoke all Beanshell scripts found in the directory defined by the 'beanShellScriptDirectory' property in the BasherContext. By default, it will try to match the files in the directory matching the regular expression 'S[0-9][0-9]{0,1}-.*\.bsh' and execute the in the order defined by the numbers by the filename.
The Beanshell interpreter is initialized with the following services:
- scheduler
- taskManager
- eventManager
- currentPhase
- currentBasherContext
The below script is an example of a script that removes all registered tasks and then adds one single task that will be run by the Basher.
tasks = taskManager.getTasks(); tasks = new java.util.ArrayList(tasks); for (java.util.Iterator itr = tasks.iterator(); itr.hasNext();) { task = itr.next(); taskManager.removeTask(task); } task = taskManager.getTaskByName("HelloWorldTask"); task.setMaxInvocations(0); task.setWeight(100); taskManager.addTask(task);