I occasionally have some long running AJAX requests in my Wicket application. When this occurs the application is largely unusable as subsequent AJAX requests are queued up to process synchronously after the current request. I would like the request to terminate after a period of time regardless of whether or not a response has been returned (I have a user requirement that if this occurs we should present the user an error message and continue). This presents two questions:
Is there any way to specify a
timeout that's specific to an AJAX
or all AJAX request(s)?
If not, is there any way to kill the current request?
I've looked through the wicket-ajax.js file and I don't see any mention of a request timeout whatsoever.
I've even gone so far as to try re-loading the page after some timeout on the client side, but unfortunately the server is still busy processing the original AJAX request and does not return until the AJAX request has finished processing.
Thanks!
I think it won't help you to let the client 'cancel' the request. (However this could work.)
The point is that the server is busy processing a request that is not required anymore. If you want to timeout such operations you had to implement the timeout on the server side. If the operation takes too long, then the server aborts it and returns some error value as the result of the Ajax request.
Regarding your queuing problem: You may consider to use asynchronous requests in spite of synchronous ones. This means that the client first sends a request for starting the long running process. This request immediately returns. Then the client periodically polls the server and asks if the process has finished. Those poll requests also return immediately saying either that the process is still running or that it has finished with a certain result.
Failed solution: After a given setTimeout I kill the active transports and restart the channel, which handles everything on the client side. I avoided request conflicts by tying each to an ID and checking that against a global reference that increments each time a request is made and each time a request completes.
function longRunningCallCheck(refId) {
// make sure the reference id matches the global id.
// this indicates that we are still processing the
// long running ajax call.
if(refId == id){
// perform client processing here
// kill all active transport layers
var t = Wicket.Ajax.transports;
for (var i = 0; i < t.length; ++i) {
if (t[i].readyState != 0) {
t[i].onreadystatechange = Wicket.emptyFunction;
t[i].abort();
}
}
// process the default channel
Wicket.channelManager.done('0|s');
}
}
Unfortunately, this still left the PageMap blocked and any subsequent calls wait for the request to complete on the server side.
My solution at this point is to instead provide the user an option to logout using a BookmarkablePageLink (which instantiates a new page, thus not having contention on the PageMap). Definitely not optimal.
Any better solutions are more than welcome, but this is the best one I could come up with.
Related
In Laravel we have an api endpoint that may take a few minutes. It's processing an input in batches and giving a response when all batches are processed. Pseudo-code below.
Sometimes it takes too long for the user, so the user navigates away and the connection is killed client-side. However, the backend processing still continues until the backend tries to return the response with a broken pipe error.
To save ressources, we're looking for a way to check after each batch if the client is still connected with a function like check_if_client_is_still_connected() below. If not, an error is raised and processing is stopped. Is there a way to achieve this ?
function myAPIEndpoint($all_batches){
$result = [];
for ($batch in $all_batches) {
$batch_result = do_something_long($batch);
$result = $result + $batch_result;
check_if_client_is_still_connected();
}
return result;
}
PS: I know async tasks or web sockets could be more appropriate for long requests, but we have good reasons to use a standard http endpoint for this.
I have tried several scenario to handle timeouts in request, but they dont seem to work.
I have passed the timeout TimeSpan when both creating the request client and/or creating the request. The request does not recive a response during the time span configured, but the task continue executing and seems hanging and no RequestTimeoutException is thrown.
What is the exact solution for handling clients timeout.
EDIT
The use case leading to the timeout is when the whole consumer service is down, so the initial request is not consumed at all. Folowing the code exemple of the request. As mentioned, i tried to pass the RequestTimeout also in the client creation. Other than this, it works perfectly when all parts are running.
var client = _busControl.CreateRequestClient<CheckRequest>(new Uri($"{rabbitHostUri}/CheckQueue"));
var response = await client.GetResponse<CheckResponse>(checkRequest, timeout: RequestTimeout.After(s: 60)).ConfigureAwait(false);
var checkResponse = response.Message;
I have a function that performs a backup every 5 seconds. From time to time the target server of the backup is not reachable and the request stops until the timeout is reached.
Since this affects the user interface I execute this 'backup function' as a async ajax request.
setInterval("doSync()", 5000 );
function doSync() {
$.ajax({
url: "backup.php",
async : true
});
};
This runs pretty good in the background.
But as soon as a reload of the page is executed, already waiting backup function calls will be completed. So in the worst case, if I have a backup with 30 seconds timeout, the user has to wait this 30 seconds before the new page is loaded.
That is not acceptable for the user.
Which strategy can I implement to avoid this?
It would be ok to terminate the backup request...
I think that issue is rather specific to the browser.
Indeed, most of them limit the number of parallel requests to the same host and that's why it "waits" before reloading the page.
If you're calling the exact same URL via your AJAX request as the one you're trying to reload, Firefox will not run more than ONE request at the same time. A simple workaround is to append a random query string to the URL.
Another option is to use the javascript beforeunload event to cancel your AJAX request : Abort Ajax requests using jQuery
I would maybe think about setting timeout in your case.
I also found similar problem already solved: click
I have an express app running with Sequelize.js as an ORM. My express app receives requests from my main Rails app, and because of the cross-domain policy, these requests are performed with getJSON.
On the client, the request is fired when the user hits a key.
Everything goes fine and express logs the queries being performed (and json being served) each time the user hits the key. Even trying to hit quickly it performs ok. But, whenever I leave the key pressed (or maybe several clients hitting the key very quickly), as it starts firing lots of requests, at some moment the server just hangs, all the requests from that point on are left pending (I see that in the Network tab of Chrome Dev Tools), and they slowly start to timeout. I have to reboot the server to make it respond again.
The server code for my request is:
models.Comment.findAllPublic(req.params.pId, req.params.sId, function(comments){
var json = comments.map(function(comment){
var com = {};
['user_id','user_avatar', 'user_slug', 'user_name', 'created_at', 'text', 'private', 'is_speaker_note'].forEach(function(key){
com[key]=comment[key];
});
return com;
});
res.json({comments: json});
});
And the findAllPublic method from the Comment model (this is a Sequelize model) is:
findAllPublicAndMyNotes: function(current_user, presentationId, slideId, cb){
db.query("SELECT * FROM `comments` WHERE commentable_type='Slide' AND commentable_id=(SELECT id from `slides` where `order_in_presentation`="+slideId+" AND `presentation_id`="+presentationId+") AND (`private` IS FALSE OR (`private` IS TRUE AND `user_id`="+current_user+" AND `is_speaker_note` IS FALSE))",self.Comment).on('success', cb).on('failure',function(err){console.log(err);});
}
How to avoid the server from getting stuck? Am I leaving some blocking code in the request that may slowly hang the server as new requests are made?
At first I thought it could be a problem because of the "forEach" when composing the json object from the Sequelize model, but I also tried leaving the callback for the mysql query empty, just responding empty json and it also got frozen.
Maybe it is a problem of the mysql connector? When the server gets stuck I can normally run the mysql console and perform queries on my database and it also responds, so I don't know if that's the problem.
I know I could just control the key event to prevent it from firing too many requests when the key gets pressed for a long time, but the problem seems to appear also when several clients hit the key repeatedly and concurrently.
Any thoughts? Thanks in advance for the help :D
Two things:
It seems like you have some path where res.render is not being called. It could be that the database you're connecting to is dropping the connection to your Express server after the absurd number of requests and the callback is never fired (and there's no database.on('close', function() { // Handle disconnect from DB, perhaps auto-restarting }) code to catch it.
Your client-side code should detect when an AJAX request on keypress is still pending while a new one is being started, and cancel the old one. I'm guessing getJSON is a jQuery method? Assuming it's jQuery's, then you need something like the following
.
var currKeyRequest = null;
function callOnKeyUp() {
var searchText = $('#myInputBox').value;
if(currKeyRequest) {
currKeyRequest.reject();
currKeyRequest = null;
}
currKeyRequest = $.getJSON('path/to/server', function(json) {
currKeyRequest = null;
// Use JSON code
});
}
This way, you reduce the load on the client, the latency of the autocomplete functionality (but why not use the jQuery UI autocomplete if that's what you're after?), and you can save the server from some of the load as well if the keypresses are faster than handshaking with the server (possible with a good touch-typist a few hours flight away).
How long can the browser wait before an error is shown before server answers for request? Can this time be unlimited?
If you are using a jQuery $.ajax call you can set the timeout property to control the amount of time before a request returns with a timeout status. The timeout is set in milliseconds, so just set it to a very high value. You can also set it to 0 for "unlimited" but in my opinion you should just set a high value instead.
Note: unlimited is actually the default but most browsers have default timeouts that will be hit.
When an ajax call is returned due to timeout it will return with an error status of "timeout" that you can handle with a separate case if needed.
So if you want to set a timeout of 3 seconds, and handle the timeout here is an example:
$.ajax({
url: "/your_ajax_method/",
type: "GET",
dataType: "json",
timeout: 3000, //Set your timeout value in milliseconds or 0 for unlimited
success: function(response) { alert(response); },
error: function(jqXHR, textStatus, errorThrown) {
if(textStatus==="timeout") {
alert("Call has timed out"); //Handle the timeout
} else {
alert("Another error was returned"); //Handle other error type
}
}
});
Yes and no. Yes the server can do it or be configured to do so, no the browsers (i dont know about version/distributor specifics) may have timeouts enabled.
There are 2 solutions though for achieving/emulating this over HTTP:
If this is simple a long running script and you're waiting for results this isnt the way to go, you should instead do as previous poster mentioned and use async processing with server polling for the results, this would be a much more sure fire solution. For example: a thumbnail script from an image processor server side: the user uploads an image, the server immediately returns a 200 and a "Job ID". The client (javascript^^) can then use the JobID to request the job status/result.
If your goal is to have something like a realtime connection between browser and server (1 way connection, once the request is made by the browser no further info can be sent without using new requests (ajax^^)), this is called long polling/reverse ajax and can be used for real-time communication over http. There are several techniques using 2 long polled requests in parallel so that once one of them timeout the second one becomes the active and the first one attempts to reconnect.
Can you explain a bit more about what you're trying to achieve - do you have a long running process on a server, do you want to change the settings on just a local machine or are you after a way to manage it for large numbers of users?
How long the browser will wait depends on a number of factors e.g. where the timeout occurs - is it at the TCP level, the server or the local browser?
If you've got a long running process on a server and you want to update a webpage afterwards the typical way to handle it is to run the long process asynchronously and notify the client when it's complete e.g. have an ajax call that polls the server, or use HTTP 1.1 and serve out a notification stream to the client.
In either case it's still possible for the connection to be closed so the client will still need the ability to re-open it.
I found, that in case of a normal (HTML page) request, browsers run to timeout after cca. 30 secs. It's important, because other participiants probably follows it: proxies, routers (do routers play in this game? I'm not sure). I am using 4 sec long server-side delay (if there's nothing to send to the client), and my AJAX client performs another HTTP request immediatelly (I am on local network, there's no internet lag). 4 sec is long enough to not to overload the server and network with frequented polls, and is short enough for the case, when somehow one poll falls out of the row which the client can't detect and handle.
Also, there're other issues with comet (long HTTP request): browser's limit on number of simultaneous HTTP request, handling of client-side events (must sent to the server immediatelly), server/network down detection and recovery, multi user handling etc.