Skip to content

Commit

Permalink
Merge pull request #4680 from natefoo/gie-client
Browse files Browse the repository at this point in the history
[17.09] Client side fixes for GIEs
  • Loading branch information
dannon committed Sep 27, 2017
2 parents 1c5c046 + 242755e commit 8230d14
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 156 deletions.
233 changes: 147 additions & 86 deletions client/galaxy/scripts/galaxy.interactive_environments.js
Expand Up @@ -17,81 +17,164 @@ function display_spinner(){
$('#main').append('<img id="spinner" src="' + galaxy_root + 'static/style/largespinner.gif" style="position:absolute;margin:auto;top:0;left:0;right:0;bottom:0;">');
}

function not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step) {
if(timeout_values.count == 0){
display_spinner();
toastr.info(
"Galaxy is launching a container in which to run this interactive environment. Please wait...",
{'closeButton': true, 'tapToDismiss': false}

/* Create a spin_state object used by spin() and spin_again() */
function make_spin_state(type, ajax_timeout_init, ajax_timeout_max, ajax_timeout_step, sleep_init, sleep_max, sleep_step, log_attempts){
var s = {
type: (typeof type !== 'undefined') ? type : "GIE spin",
ajax_timeout: (typeof ajax_timeout_init !== 'undefined') ? ajax_timeout_init : 2000,
ajax_timeout_max: (typeof ajax_timeout_max !== 'undefined') ? ajax_timeout_max : 16000,
ajax_timeout_step: (typeof ajax_timeout_step !== 'undefined') ? ajax_timeout_step : 500,
sleep: (typeof sleep_init !== 'undefined') ? sleep_init : 500,
sleep_max: (typeof sleep_max !== 'undefined') ? sleep_max : 8000,
sleep_step: (typeof sleep_step !== 'undefined') ? sleep_step : 100,
log_attempts: (typeof log_attempts !== 'undefined') ? log_attempts : true,
count: 0,
}
return s;
}


/* Log/display an error when spinning fails. */
function spin_error(console_msg, user_msg, clear){
console.log(console_msg);
if(clear) clear_main_area();
if(typeof user_msg == "string"){
toastr.clear();
toastr.error(
user_msg,
"Error",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
}
timeout_values.count++;
if(timeout_values.time < timeout_time_max){
timeout_values.time += timeout_time_step;
}


/* Increase sleep time and spin again. */
function spin_again(spin_state){
if(spin_state.sleep < spin_state.sleep_max){
spin_state.sleep += spin_state.sleep_step;
}
if(spin_state.log_attempts){
console.log(spin_state.type + " request " + spin_state.count + " request timeout " + spin_state.ajax_timeout + "ms sleeping " + spin_state.sleep / 1000 + "s");
}
console.log("Readiness request " + timeout_values.count + " sleeping " + timeout_values.time / 1000 + "s");
window.setTimeout(timeout, timeout_values.time)
window.setTimeout(spin_state.spinner, spin_state.sleep);
}

/**
* Check a URL for a boolean true/false and call a callback when done.

/*
* Spin on a URL as long as it times out, otherwise, call the provided success or error callback. If the callback
* returns `true`, the condition is considered "resolved" and spinning stops. Otherwise, continue spinning, increasing
* AJAX timeouts and/or sleep values as configured in the spin_state.
*/
function load_when_ready(url, success_callback){
var ajax_timeout = 500;
var ajax_timeout_max = 10000;
var ajax_timeout_step = 250;
var timeout_values = { time: 1000, count: 0 };
var timeout_time_max = 15000;
var timeout_time_step = 1000;
var timeout = function(){
$.ajax({
function spin(url, bool_response, success_callback, timeout_callback, error_callback, spin_state){
var spinner = function(){
var ajax_params = {
url: url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: ajax_timeout,
dataType: "json",
success: function(data){
if(data == true){
console.log("Galaxy reports IE container ready, returning");
clear_main_area();
toastr.clear();
success_callback();
}else if(data == false){
not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step);
}else{
clear_main_area();
toastr.clear();
toastr.error(
"Galaxy failed to launch a container in which to run this interactive environment, contact your administrator.",
"Error",
{'closeButton': true, 'tapToDismiss': false}
);
}
timeout: spin_state.ajax_timeout,
success: function(data, status, jqxhr){
if(!success_callback(data, status, jqxhr)) spin_again(spin_state);
},
error: function(jqXHR, textStatus, errorThrown){
if(textStatus == "timeout"){
if(ajax_timeout < ajax_timeout_max){
ajax_timeout += ajax_timeout_step;
error: function(jqxhr, status, error){
if(status == "timeout"){
if(spin_state.ajax_timeout < spin_state.ajax_timeout_max){
spin_state.ajax_timeout += spin_state.ajax_timeout_step;
}
not_ready(timeout, timeout_values, timeout_time_max, timeout_time_step);
spin_state.count++;
if(!timeout_callback(jqxhr, status, error)) spin_again(spin_state);
}else{
clear_main_area();
toastr.clear();
toastr.error(
"Galaxy encountered an error while attempting to determine the readiness of this interactive environment, contact your administrator.",
"Error",
{'closeButton': true, 'tapToDismiss': false}
);
spin_state.count++;
if(!error_callback(jqxhr, status, error)) spin_again(spin_state);
}
}
});
},
}
if(bool_response) ajax_params["dataType"] = "json";
$.ajax(ajax_params);
}
console.log("Setting up new spinner for " + spin_state.type + " on " + url);
spin_state.spinner = spinner;
window.setTimeout(spinner, spin_state.sleep);
}


/*
* Spin on a URL forever until there is an acceptable response.
* @param {String} url: URL to test response of. Must return a 200 (302->200 is OK).
* @param {Boolean} bool_response: If set to `true`, do not stop spinning until the response is `true`. Otherwise, stop
* as soon as a successful response is received.
*/
function spin_until(url, bool_response, messages, success_callback, spin_state){
var warn_at = 40; // ~2 mins
var message_once = function(message, spin_state){
if(spin_state.count == 1){
display_spinner();
toastr.info(
message,
null,
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
}
}
window.setTimeout(timeout, timeout_values.time);
var wrapped_success = function(data){
if(!bool_response || (bool_response && data == true)){
console.log(messages["success"]);
clear_main_area();
toastr.clear();
success_callback();
}else if(bool_response && data == false){
message_once(messages["not_ready"], spin_state);
return false; // keep spinning
}else{
spin_error("Invalid response to " + spin_state.type + " request", messages["invalid_response"], true);
}
return true; // stop spinning
}
var timeout_error = function(jqxhr, status, error){
message_once(messages["waiting"], spin_state)
if(spin_state.count == warn_at){
toastr.warning(
messages["wait_warn"],
"Warning",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
}
return false; // keep spinning
}
spin(
url,
bool_response,
wrapped_success,
timeout_error,
timeout_error,
spin_state
);
}


/**
* Test a boolean (json) response from a URL, and call a callback when done.
* http://stackoverflow.com/q/25390206/347368
* @param {String} url: URL to test response of. Must return a 200 (302->200 is OK) and either `true` or `false`.
* @param {String} callback: function to call once successfully connected.
*
*/
function load_when_ready(url, success_callback){
var messages = {
success: "Galaxy reports IE container ready, returning",
not_ready: "Galaxy is launching a container in which to run this interactive environment. Please wait...",
unknown_response: "Galaxy failed to launch a container in which to run this interactive environment, contact a Galaxy administrator.",
waiting: "Galaxy is launching a container in which to run this interactive environment. Please wait...",
wait_warn: "It is taking an usually long time to start a container. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error: "Galaxy encountered an error while attempting to determine the readiness of this interactive environment's container, contact a Galaxy administrator."
}
var spin_state = make_spin_state("IE container readiness");
spin_until(url, true, messages, success_callback, spin_state);
}


/**
* Test availability of a URL, and call a callback when done.
Expand All @@ -101,34 +184,12 @@ function load_when_ready(url, success_callback){
*
*/
function test_ie_availability(url, success_callback){
var request_count = 0;
display_spinner();
interval = setInterval(function(){
$.ajax({
url: url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: 500,
success: function(){
console.log("Connected to IE, returning");
clearInterval(interval);
success_callback();
},
error: function(jqxhr, status, error){
request_count++;
console.log("Availability request " + request_count);
if(request_count > 30){
clearInterval(interval);
clear_main_area();
toastr.error(
"Could not connect to IE, contact your administrator",
"Error",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false}
);
}
}
});
}, 1000);
var messages = {
success: "IE connection succeeded, returning",
waiting: "Interactive environment container is running, attempting to connect to the IE. Please wait...",
wait_warn: "It is taking an usually long time to connect to the interactive environment. Attempts will continue but you may want to report this condition to a Galaxy administrator if it does not succeed soon.",
error: "An error was encountered while attempting to connect to the interactive environment, contact your administrator."
}
var spin_state = make_spin_state("IE availability");
spin_until(url, false, messages, success_callback, spin_state);
}
Expand Up @@ -38,6 +38,7 @@ function load_notebook(password, notebook_login_url, notebook_access_url){
// we've successfully connected to the IE.
test_ie_availability(notebook_login_url, function(){
_handle_notebook_loading(password, notebook_login_url, notebook_access_url);
keep_alive();
});
}

Expand All @@ -48,34 +49,44 @@ function keep_alive(){
* this function is not constantly pinging the container, the container will
* terminate itself.
*/

var request_count = 0;
interval = setInterval(function(){
$.ajax({
url: notebook_access_url,
xhrFields: {
withCredentials: true
},
type: "GET",
timeout: 500,
success: function(){
console.log("Connected to IE, returning");
},
error: function(jqxhr, status, error){
request_count++;
console.log("Request " + request_count);
if(request_count > 30){
clearInterval(interval);
clear_main_area();
toastr.error(
"Could not connect to IE, contact your administrator",
"Error",
{'closeButton': true, 'timeOut': 20000, 'tapToDismiss': false}
);
}
}
});
}, 30000);
var warn_at = 4;
var count_max = 60;
// we sleep 15 seconds between requests and the default timeout for the Jupyter container is 120 seconds, so start
// with a pretty high ajax timeout. sleep starts low because we want to get the warning up pretty quickly
var spin_state = make_spin_state("IE keep alive", 8000, 16000, 2000, 5000, 15000, 5000, false);
var success = function(){
console.log("IE keepalive request succeeded");
toastr.clear()
if(spin_state.count >= warn_at){
toastr.clear();
toastr.success(
"Interactive environment connection restored",
{'closeButton': true, 'timeOut': 5000, 'extendedTimeOut': 2000, 'tapToDismiss': true}
);
}
spin_state.count = 0;
spin_state.timeout_count = 0;
spin_state.error_count = 0;
return false; // keep spinning
}
var timeout_error = function(jqxhr, status, error){
console.log("IE keepalive request failed " + spin_state.count + " time(s) of " + count_max + " max: " + status + ": " + error);
if(spin_state.count == warn_at){
toastr.warning(
"Your browser has been unable to contact the interactive environment for "
+ spin_state.count + " consecutive attempts, if you do not reestablish "
+ "a connection, your IE container may be terminated.",
"Warning",
{'closeButton': true, 'timeOut': 0, 'extendedTimeOut': 0, 'tapToDismiss': false}
);
return false; // keep spinning
}else if(spin_state.count >= count_max){
spin_error("IE keepalive failure limit reached", "Lost connection to interactive environment, contact your administrator", false);
return true; // stop spinning
}
}
console.log("IE keep alive worker starting");
spin(notebook_access_url, false, success, timeout_error, timeout_error, spin_state);
}


Expand Down
Expand Up @@ -58,12 +58,6 @@ var notebook_login_url = '${ notebook_login_url }';
var notebook_access_url = '${ notebook_access_url }';
${ ie.plugin_require_config() }
// Keep container running
requirejs(['interactive_environments', 'plugin/jupyter'], function(){
keep_alive();
});
// Load notebook
requirejs(['interactive_environments', 'plugin/jupyter'], function(){
Expand Down

0 comments on commit 8230d14

Please sign in to comment.