It's no fun when an unbalanced server workload causes problems. So what can you do about it?
Sometimes, you get surprises when you're programming things for different platforms.
Normally, I fetch the data I use in my web applications from the IBM i, but some time ago I was trying to get some data from an MS SQL database, and it gave me some challenges that I didn't expect.
I'm not saying MS SQL server is bad, but in some ways it just works differently from the IBM i database. Simple as that.
The MS SQL database that was serving the data to me was pretty busy; it had a lot of constant activity going on all the time. I was retrieving the data using a stored procedure that I wasn't responsible for, so I just got a handle to pull in and out of the a dataset containing the data in XML format, which I then would parse and use on the web page. Pretty straightforward.
I use jQuery for this kind of thing. I don't really care if the server in the background is IBM i, Linux, or MS server; it's all the same to me because all I do is use a jQuery AJAX call to get the data and then parse the result, whether it's XML, JSON, or HTML I get back.
Sometimes, I have to fire a lot of AJAX calls at once because I get information from various files and systems. On the IBM i, it had never caused a problem. However, it did for the MS SQL server: it had deadlocks, it had timeouts, and the poor person who was making the stored procedure was really going out of his mind. The problem he told me was that I was firing multiple AJAX calls at the server at one time. That caused the database to lock, so the data was never sent back to me.Therefore, I had to somehow balance the AJAX calls so that when one was done, the next would start.
JavaScript (jQuery) works asynchronously, which means that when you execute a function the browser will just continue with the next line in your code, and that was what I had to control. I looked at the "async" option in the AJAX API, but it didn't seem to fulfill what I needed (maybe I was too stupid to really understand the documentation). So instead I invented my own cascading technique and found that it worked pretty well.
I used the AJAX complete callback function to control it. The complete callback function is called after the "success" and "error" callbacks are executed, so that is where I put my code to control the "cascade of calls."
In my example, I have created a "lightweight" version, which I hope will give you an idea of how you can prevent the server in the background to be "bombed" by multiple AJAX calls.
What Does the Example Do?
The example I have created will do the following:
- Fire 10 AJAX calls in a cascade and then wait 30 seconds before starting all over again.
- The AJAX call itself will just retrieve some very simple XML that has a quantity and price node that will be parsed out and placed in some <div> boxes on the web page.
- While executing, an "activebar" will be shown to identify which box is being updated.
- hen done, a total box will be updated with the sums of the quantity and the price.
I used some inline CSS to spice up the look and feel of the page to make it look a little pretty.
Figure 1: Here's the cascade screen.
I defined the boxes that should hold the data using a <div> tag with a CSS class. They all have an ID of ID01 to ID10 and are placed within a <table> to spread them easily across the page. Look at the example code to see that:
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td>
<div class="box" id="next_out_data_ID01"></div>
</td>
<td>
<div class="box" id="next_out_data_ID02"></div>
</td>
<td>
<div class="box" id="next_out_data_ID03"></div>
</td>
<td>
<div class="box" id="next_out_data_ID04"></div>
</td>
<td>
<div class="box" id="next_out_data_ID05"></div>
</td>
<td>
<div class="box" id="next_out_data_ID06"></div>
</td>
<td>
<div class="box" id="next_out_data_ID07"></div>
</td>
<td>
<div class="box" id="next_out_data_ID08"></div>
</td>
<td>
<div class="box" id="next_out_data_ID09"></div>
</td>
<td>
<div class="box" id="next_out_data_ID10"></div>
</td>
The active bar is defined the same way, so it's straightforward.
I defined a function called GetServerData, which takes only one parameter: the ID of the <div> box I want to update.
I have named all the XML files with the same name as the IDs, but in a real-life situation, you will call an RPG CGI program, a PHP script, or something else to give you the data result.
The XML data for each ID is as shown below—of course, with different values for quantity and price.
<?xml version="1.0"?>
<data>
<qty>150</qty>
<price>3152.10</price>
</data>
That's pretty much the setup for simulating the data fetching. Now, we're ready to look at the hard bit, the AJAX call and the handling of the cascading.
The AJAX Call
When you look at the code below, it might seem a little "wow," but it's pretty straightforward. Let me walk you through it so you can see it isn't that hard.
function GetServerData(reference ) {
A)
if ( $("#reset_totals").val() == 'Y' ) {
$("#reset_totals").val('N');
total_qty = 0
total_price = 0
$(".activebar").removeClass('activeColor');
$("#total_data").html('Working...');
}
B)
// Create XML fil name
var currentURL = 'xmldata/' + reference + '.xml';
C)
$.ajax({
type: "GET",
url: currentURL,
dataType: "xml",
cache: false,
success: function(xml) {
$(xml).find('data').each(function() {
D)
// Get Qty and price and calcualte totals and likewise
var qty = $(this).find('qty').text();
var price = $(this).find('price').text();
var total = parseFloat(qty) * parseFloat(price);
total_qty = parseFloat(total_qty) + parseFloat(qty);
total_price = parseFloat(total_price) + parseFloat(total);
// Format numbers - you must parse them to float to make sure JavaScript knows what to do...
total = parseFloat(total).toFixed(2);
total_qty = parseFloat(total_qty).toFixed(2);
total_price = parseFloat(total_price).toFixed(2);
$("#next_out_data_" + reference).html('Reference: <b>' + reference + '</b><hr>QTY: ' + qty + '<br>Price: ' + price + '<hr>Total: ' + total);
// Set action indicator
$("#active_" + reference).addClass('activeColor');
});
},
complete: function() {
E)
if ( reference == 'ID01' ) {
reference = 'ID02'
$("#msg").html('Working...');
}
else if ( reference == 'ID02' ) {
reference = 'ID03'
}
else if ( reference == 'ID03' ) {
reference = 'ID04'
}
else if ( reference == 'ID04' ) {
reference = 'ID05'
}
else if ( reference == 'ID05' ) {
reference = 'ID06'
}
else if ( reference == 'ID06' ) {
reference = 'ID07'
}
else if ( reference == 'ID07' ) {
reference = 'ID08'
}
else if ( reference == 'ID08' ) {
reference = 'ID09'
}
else if ( reference == 'ID09' ) {
reference = 'ID10'
}
else {
F)
reference = 'ID01'
setTimeout(function(){ GetServerData(reference) },30000)
$("#msg").html('Done and ready for next retrieval...');
$("#total_data").html('Total QTY: <b>' + total_qty + '</b><br>Total Price: <b>' + total_price) + '</b>';
$("#reset_totals").val('Y');
return;
}
GetServerData(reference);
},
error: function() {
$('#div_error').html('<center><b>Error retrieving data....</b></center>');
}
});
}
OK! Let's dive into it and see what really happens.
I have highlighted some of the key points—from A to F—and will explain what goes on.
- When the "reset_totals" ID has a value of 'Y', a new round is about to begin. This means that totals are to be reset, the active meter is reset, and a progress message is sent to the screen.
- Here, the name of the XML file is created based on the ID passed to the function in the reference variable.
- The actual AJAX call happens here. Business as usual.
- The XML data is parsed, totals are calculated, and based on the ID in the reference variable, the values are written the <div> box in question. Note I am using the next_out_data + reference to define the ID of the box. The active meter ID is updated also based on the ID and will show a nice green color.
- Here, the cascading is "calculated," depending on the ID in the reference variable. Of course, the cascade controlling could have been done in some loops, but I think the if/else coding makes it very easy to understand and even very easy to expand if ever needed.
- When the cascading has been done for all 10 boxes, the GetServerData server data will end here and go into sleep mode for 30 seconds using the setTimeout JavaScript function. Upon waking up again, the GetServerData will be called in n recursive call.
Note that JavaScript, unlike RPG, accepts recursive calls right away and doesn't really need any special considerations as far as I am aware of.
A Final Word
Well, that's pretty much it. I hope you understand what I'm trying to show and also see the need for this kind of fetching of data. Of course, this is also a good technique if you're calling web services or long-running programs on your IBM i.
About the total box: Well, just for fun, I placed it in the right corner, but just change the "totalbox" CSS class to place it somewhere else or even just remove the class from the total box.
You can see the cascading page in real life here.
And you can download the code here. If you have a localhost set up, just unzip it and start the ex1.htm file. It should work straight off.
Till next time, keep getting good ideas, share them, and let other people stand on your shoulders to make our programs even better in the fast-changing world we are facing every day.
Links of Interest
ILE RPG Programmer's Guide - Recursive Calls
LATEST COMMENTS
MC Press Online