Creating a re-usable library of routines using SOASTA

Creating reusable load testing functions using SOASTA

This article describes a framework for creating a reusable library of functions using the SOASTA toolset . It amazing that SOASTA doesn’t allow you to do this out of the box. So after a little hacking around, I discovered a way of achieving this.  It made my life and coding a LOT easier.  This is a hacky, and is unsupported by SOASTA.  I’m basically exploiting a feature of the Rhino compiler which wasn’t intended.  I’ve used this extensively and  had no problems so far.  Take a look at the guidelines at the bottom of this article, these outline a set of good practices which should help you avoid any potential pitfalls with this unsupported method.

Basic Method of working:

Everything in JavaScript is an object, JS is a little loose so the paradigm allows you to be a little naughty and do things you couldn’t otherwise do in other more strongly typed languages.  I attempted to attach methods  to the DOM and other objects etc and see if I could reference them further down within the Clip with JS scripts. Finally I attached to a clip object – this worked. There are other objects you can attach to, but this isn’t recommended. You are likely to get into threading, contention and locking issues. Suffice to say, the safest object to attach methods onto is the clip object.  So a simple code example:

//Create a library of functions once within your clip

function myMethod( value ){
//Put any commmon execution code for your function in here - without having to re-paste it through the clip
return value*value;
}

function myMethod2( value, value2 ){
//Put any common execution code for your function in here - without having to re-paste it through the clip
return value*value2;
}

function myMethod3( value, value2, value3 ){
//Put any common execution code for your function in here - without having to re-paste it through the clip
return value*value2*value3;
}

/*********************************************************************************
* JB: This is the Hacky Bit - attach your functions to the current clip object
* This will be confined to the scope this clip and therefore to that particular
* VU. Meaning we are thread safe.
* DO NOT attempt to set against any objects that are visible
* and shared above the clip level e.g. Prop.
*
*********************************************************************************/
var addMethods = $context.currentClip;
addMethods.myMethod = myMethod; //
addMethods.myMethod2 = myMethod2; //
addMethods.myMethod3 = myMethod3; //

//Cheeky little tester; are we working?
var returnValue= $context.currentClip.myMethod(101010101010);
//Just Output
$context.result.postMessage($context.result.LEVEL_INFO, "MyMethod One returns" + returnValue);

So now, we have created a very simple library of functions called myMethod1(),myMethod2(),myMethod3().

The clever bit that is making these functions global to the rest of the clip are the lines :

 var addMethods = $context.currentClip;

addMethods.myMethod = myMethod; // 

1) The first line creates a reference to the $context.currentClip object. This has been simply done for the purposes of readablity.

2) The second line does the magic and adds the myMethod method (or your function) to the $content.currentClip object.  Notice: The arguments suppled for the created method do not have to be supplied – JS is looser than a wizards sleeve!

Note: Within the clip editor you do not have to change the scope of this JS script.

So now, if we want to call any of the methods declared in the above library within a completely separate pieceof JavaScript code we just have to do the following:

//Set up a reference to the library of functions
var mylib = $context.currentClip;

//Make the call your Custom Routine 1, get back your result
var mymethod1 = mylib.myMethod(20 * vuID);

//Make the call whatever your Custom Routine 2 is, get back your result
var mymethod2 = mylib.myMethod2(20,10);

//Make the call whatever your Custom Routine 3 is, get back your result ... goes on
var mymethod3 = mylib.myMethod3(20,10,10);

Simple, but very powerful.  We can now do neat things like hide complexity, We can set up static variables and remember state within the library (useful for things switching between different logging states such as debug,info, error messages).   We can also write and consolidate our routines in a way that are easily shared.   I’ve appended a more complete example of a library function to this article as an example.

I’ve created a link to a more complete and ongoing set of load testing library routines I have developed  here

Questions:

Q) Is this safe to use …?
A) I’ve tested on my CT Lite, and thrashed some of the methods in an attempt to cause contention and bring out threading issues.  It appears to be fine… but we need to test on a larger project to gain confidence.

Q) Isn’t this slow? Does it cause an overhead by causing the library to be redefined if you iterate within a clip?
A) Good question – I’ve checked and the initial execution time to run through the library is high, but oddly the subsequent execution time through the library is in the order of low 1-2 MS.  Further – any calls to the functions within the library are around 1ms.  This appears to be quicker.  In fact it could even be faster and more efficient than the current method – as the methods are already compiled and then simply referenced.  But the honest answer is I’m not completely 100% – we don’t quite understand whats happening behind the scenes on the Rhino compiler.  While this result is good its not what I expected (I expected worst) – I’m always a little suspicious when I don’t have a complete understanding of whats happening behind the scenes.

Q) Is there anything I shouldn’t do?
A) I recommend that you avoid referencing anything directly within the library (or create direct links to large data structures).  Keep the library as stateless as possible e.g. Do not reference a seed Data object within the library, better to pass it in as a reference to a function of the library.  Also, I recommend would avoid directly referencing things like:

var msg = $context.currentItem.previousItem;
var origText = msg.getResponse(msg.RESPONSE_HTTP_BODY);

within the library.  You should get the response in your JS and then pass it into the library of functions as a argument.

An easy rule of thumb to follow:: “Is this function stand alone and generic, could I give this to someone else to use out of the box without them having to change a thing within the library”.  Follow this rule when writing your functions and you shouldn’t go far wrong.  This rule is designed to initially minimise the risk of something going wrong within the compiler.

Q. If I find an issue with this method, should I approach SOASTA?
A. Not really; they are currently overwhelmed and this ‘feature’ isn’t supported. Follow the guildlines above, then attempt to resolve this yourself before going to support (with a very focussed problem).  You don’t want to hack them off by making them attempt to solve issues on unsupported features.  If they find this generates more issues than it solves they it is possible for them to disable this in the Rhino compiler.

Q. Should I annotate my code?
A. Yes! It more important for people to understand your code than it is for it to work correctly.  This means others can spot issues faster (every piece of code has bugs) and contrubute, enhance and correct. It’s more important for your code to be easily understood than it is for it to be ‘fast’.

Q. Coding standards
A. Everyone has different standards, but its important to have a consistant standard.  I’ve started by having clear defs at the beginning of the functions, only one exit point per function and no direct internal references.  Its just a suggested starting point

I’ve started creating a more generic library of functions to common problems.

Can be found here:: https://www.dropbox.com/sh/988n8ibjkl7wr51/tB86wqMnIJ

I’ll start a code bin for versioning of a generic library if demand dictates.

Happy Load Testing.

Jason

Leave a Reply

Your email address will not be published. Required fields are marked *