A recent client engagement presented me with an interesting challenge when scripting for a performance test of Cognos (version 10.1). Cognos uses asynchronous communication between the client and Cognos server; which is essentially polling by the client to the server, to determine the progress of a report being generated. Rather than making a request and waiting patiently for the server to return the generated report, the client will send a separate request every few seconds until the report is returned. Anyone who has driven on a family holiday with children yelling “are we there yet” will be familiar with this.

So what this means from a scripting perspective is that your script needs to be quite dynamic for 3 reasons:

  1. You need to cater for fast response times, which will only require 1 request per step
  2. You need to cater for long response times once the load increases, which will require a variable number of requests per step
  3. You need to cater for correlation and text checks when the complete response comes through, however you’ll never know which request to register them for.

During a report request step, the client (VuGen) will typically make 2 types of requests; a ‘run’ or ‘wait’ request, and the server will respond with one of 3 types of status; ‘working’, ‘stillworking’ or ‘complete’. The request type can be identified by the request parameter ‘ui.action’, which will be in the body of the request:

web_custom_request("cognosisapi.dll_5_WaitLoop",
   "URL=http://{pServerName}/cognos/cgi-bin/cognosisapi.dll",
   "Method=POST",
   "TargetFrame=",
   "Resource=0",
   "RecContentType=text/plain",
   "Referer=http://{pServerName}/cognos/...&ui.action=run&ui.object...
   "Snapshot=t8.inf",
   "Mode=HTML",
   "Body=…cv.actionState={actionstate_Type1_1}&ui.action=wait&ui.object=...
   LAST);

(Be careful not to confuse this with the ui.action of the ‘Referer’ item in the request)

The response status will be near the start of the response after the text ‘status’. This can display enclosed in either quotation marks, or the words “quot”, so if you don’t find one, search for the other:

"status": "working",

or:

"status": "working",

As mentioned above, this relates specifically to Cognos v10.1, I’m know it is also true for version 8, and I believe this is the same if not similar to other versions (don’t hold me to this).

In order for your script to cater for each of the above points, every step that incorporates some sort of report generation e.g. not logins or navigation, will require the original ‘run’ request, as well as the ability to run 1 or more ‘wait’ requests, depending on the response status. A new request is sent around every 3 seconds:

Client request (ui.action) Traffic Server Response (status)
Request with   immediate response:
run Request a report
Report is returned complete
Request with 1   wait cycle:
run Request a report
Generating report… working
wait Waiting for report
Report is returned complete
Request with   many wait cycles:
run Request a report
Generating report… working
wait Waiting for report
Still generating report… stillworking
wait Still waiting for report
Still generating report… stillworking
*Repeat until report is returned
wait Still waiting for report
Report is returned complete

When recording the script initially, you will need to ensure that each ‘run’ request will also generate at least 1 ‘wait’ request, so that you can capture this and duplicate it as needed. As the requests send about every 3 seconds, this means you need to ensure that each request will take at least 3 seconds while recording. This can be done by changing the following config settings on the cognos server/s:

  1. In each instance of Cognos, locate a file called rsvpproperties.xml.sample in the “configuration” directory.
  2. Rename it to rsvpproperties.xml
  3. Using an editor, uncomment, and edit the AsynchWaitOverride property within the file like so: <property>AsynchWaitOverride</property> <value type=”long”>-3</value>
  4. Restart Cognos

What this setting does is “sleeps” the initial run request for 1 second longer than the value of AsynchWaitOverride, which forces the async conversation to begin for at least one cycle. To change this setting back rename the rsvpproperties.xml back to rsvpproperties.xml.sample, and re-comment the AsynchWaitOverride property within the file. (Ensure you change the setting back after recording the script!)

Now that you’ve captured a ‘run’ request and at least 1 ‘wait’ request for each step, you can go through each step and apply some logic and functions that will enable you to remove all of the extra (2nd and more) ‘wait’ request that you may have initially recorded. In your script, as the complete response could come back after the first ‘run’ request, you need to assess whether this has happened or not, and if not, continue executing the ‘wait’ response until it does. The pseudo code should look something like this:

  • Set web_reg_save_param(ResponseStatus)
  • Get Report (ui.action=run)
  • If ResponseStatus != “Complete” Then
    • Do Until ResponseStatus = “Complete”
      • Set web_reg_save_param(ResponseStatus)
      • Get Report (ui.action=wait)
      • Loop
    • End If

This caters for fast or slow response times, however there is still the issue of correlation and text checks that may or may not appear after each request. My quick and nasty solution was to register all possible correlation and text checks before every request, but to use SaveCount for web_reg_find, and set “Notfound=warning” for each web_reg_save_param to stop the script from failing whenever a value is not found.

To ensure each step has been successfully however, a combined assessment of the response status returning as ‘complete’ as well as a web_reg_find SaveCount of ‘>0’ needs to pass. By including these concepts, the above pseudo code now looks like this for each step:

  • Set web_reg_save_param(ResponseStatus)
  • Set web_reg_save_param(AllPossibleCorrelations)
  • Set web_reg_find(StepSuccessfulSaveCount)
  • Get Report (ui.action=run)
  • If ResponseStatus != “Complete” Then
    • Do Until ResponseStatus = “Complete”
      • Set web_reg_save_param(ResponseStatus)
      • Set web_reg_save_param(AllPossibleCorrelations)
      • Set web_reg_find(StepSuccessfulSaveCount)
      • Get Report (ui.action=wait)
      • Loop
    • End If
    • If StepSuccessfulSaveCount = 0 Then
      • Lr_error_message(“Response returned ‘complete’, but was not successful”)
    • End If

Now obviously this is going to get a bit messy listing all these ‘web_reg…’ steps for every report-generating step in your script, so the obvious solution is to create functions to handle this. As I was a bit under the pump, I only had time to create functions to handle the status and web_reg_find assessment as it’s identical every time. I’d imagine that the whole process could be built into functions with variable information passing in as parameters to tidy things up a bit though.

This function was used to grab the status of each response, using a function just made the script a bit tidier as I was using it several times for every request:

void GetResponseStatus()
{
   web_reg_save_param_ex(
   "ParamName=ResponseStatus",
   "LB=status&amp;quot;: &amp;quot;",
   "RB=&amp;quot;, ",
   "Notfound=warning",
   SEARCH_FILTERS,
   "Scope=Body",
   "IgnoreRedirections=Yes",
   "RequestUrl=*/cognosisapi.dll*",
   LAST);
}

This function was called after every request, to assess if the response is still working. If the response is complete, it then assesses if the previously defined web_reg_find was successful:

void CheckResponseAndSuccess()
{
   if (strcmp(lr_eval_string("{ResponseStatus}"), “complete”) != 0){
      lr_output_message("**** Still Working response ****");
   }
 
   if (strcmp(lr_eval_string("{ResponseStatus}"), “complete”) == 0){
      if (atoi(lr_eval_string("{WebRegFindSuccessful}")) &gt; 0){
         lr_output_message("**** WebRegFind Successful ****");
      } else{
           lr_error_message("**** WebRegFind Failed!! ****");
      }
   }
}

As the text for each web_reg_find will be variable, ideally this could be passed into the two functions as a parameter. This would allow you to include the web_reg_find in the first function, as well as report the string that wasn’t found in the second function for more accurate error reporting.

2 comments on “Asynchronous Communication: Scripting For Cognos

  1. Hi Adam,

    Great work. I have a requirement in cognos reports.

    I need to call a java/.net program after report generation.

    Could you please give some tips to solve this.

  2. Nice post Adam. I am sure it will come in handy if I ever have to do a Cognos script.

    I recently had to do a some vugen scripting for an application that would also poll and wait for a response. You didn’t mention it, but it is far better to use sleep(milliseconds); rather than lr_think_time(seconds) as this allows you to change your think-time multiplier without wrecking your polling loop. There is more information in this tech tip that Stu wrote a while back: http://www.jds.net.au/tech-tips/think-time-that-cannot-be-ignored/

Leave a Reply