diff --git a/admin/Gemfile b/admin/Gemfile index 716543c5e..21e2c819a 100644 --- a/admin/Gemfile +++ b/admin/Gemfile @@ -117,4 +117,4 @@ end gem 'pry' gem 'pry-remote' gem 'pry-stack_explorer' -gem 'pry-debugger' +#gem 'pry-debugger' diff --git a/admin/app/admin/connections.rb b/admin/app/admin/connections.rb index b97e71164..92cfb668e 100644 --- a/admin/app/admin/connections.rb +++ b/admin/app/admin/connections.rb @@ -91,6 +91,7 @@ ActiveAdmin.register JamRuby::Connection, :as => 'Connection' do row :locidispid row :aasm_state row :udp_reachable + row :is_network_testing row :scoring_failures row :scoring_timeout_occurrences row :scoring_failures_offset diff --git a/admin/app/admin/scoring_load.rb b/admin/app/admin/scoring_load.rb index e020a4316..f6f3e3b2c 100644 --- a/admin/app/admin/scoring_load.rb +++ b/admin/app/admin/scoring_load.rb @@ -5,7 +5,7 @@ ActiveAdmin.register_page "Current Scoring Load" do table_for GetWork.summary do column "Work", :work_count column "Who", Proc.new { |connection| "#{connection.first_name} #{connection.last_name} - #{connection.email}" } - column "Errors", Proc.new { |connection| "#{connection.udp_reachable != false ? "" : "No STUN,"} #{connection.in_timeout != 'f' ? "Timeout," : ""} #{connection.in_session != 'f' ? "In-Session," : ""}" } + column "Errors", Proc.new { |connection| "#{connection.udp_reachable != false ? "" : "No STUN,"} #{connection.is_network_testing != false ? "NETWORK TESTING" : ""} #{connection.in_timeout != 'f' ? "Timeout," : ""} #{connection.in_session != 'f' ? "In-Session," : ""}" } column "Total Timeouts", :scoring_timeout_occurrences column "Current Scoring Failures", :scoring_failures column "Offset", :scoring_failures_offset diff --git a/db/manifest b/db/manifest index 5e0250284..076b72aa0 100755 --- a/db/manifest +++ b/db/manifest @@ -216,4 +216,5 @@ fix_find_session_sorting_2216c.sql entabulate_current_network_scores.sql discard_scores_changed.sql emails_from_update.sql -add_active_feed.rb \ No newline at end of file +add_active_feed.rb +connection_network_testing.sql \ No newline at end of file diff --git a/db/up/connection_network_testing.sql b/db/up/connection_network_testing.sql new file mode 100644 index 000000000..3cd9e78df --- /dev/null +++ b/db/up/connection_network_testing.sql @@ -0,0 +1,44 @@ +-- let the server know if the client is network testing. If so, then also remove them from work +ALTER TABLE connections ADD COLUMN is_network_testing BOOLEAN DEFAULT FALSE NOT NULL; + +DROP FUNCTION IF EXISTS get_work (my_client_id VARCHAR(64), mylocidispid BIGINT, myaddr BIGINT, return_rows INT, stale_score INTERVAL); +CREATE FUNCTION get_work (my_client_id VARCHAR(64), mylocidispid BIGINT, myaddr BIGINT, return_rows INT, stale_score INTERVAL) RETURNS TABLE (client_id VARCHAR(64)) VOLATILE AS $$ +BEGIN + RETURN QUERY WITH + scorable_locations AS ( + SELECT DISTINCT locidispid FROM connections WHERE client_type = 'client' AND connections.client_id != my_client_id AND addr != myaddr AND udp_reachable AND is_network_testing = FALSE AND NOW() > scoring_timeout AND connections.music_session_id IS NULL AND + locidispid NOT IN (SELECT DISTINCT blocidispid FROM most_recent_scores WHERE alocidispid = mylocidispid AND (current_timestamp - score_dt) < stale_score) AND + locidispid/1000000 IN (SELECT locid FROM geoiplocations WHERE geog && st_buffer((SELECT geog FROM geoiplocations WHERE locid = mylocidispid/1000000), 4023360)) + ) + + SELECT tmp.client_id FROM (SELECT connections.client_id, random() AS r, row_number() OVER (PARTITION BY connections.locidispid) AS rownum FROM connections, scorable_locations + WHERE connections.locidispid = scorable_locations.locidispid AND client_type = 'client' AND connections.client_id != my_client_id AND addr != myaddr AND udp_reachable AND NOW() > scoring_timeout AND connections.music_session_id IS NULL ) tmp WHERE rownum <= 1 ORDER BY r LIMIT return_rows; + + RETURN; + +END; +$$ LANGUAGE plpgsql; + + +DROP FUNCTION IF EXISTS get_work_summary (stale_score INTERVAL); +CREATE FUNCTION get_work_summary (stale_score INTERVAL) RETURNS TABLE (work_count BIGINT, client_id VARCHAR(64), email VARCHAR, first_name VARCHAR, last_name VARCHAR, user_id VARCHAR(64), udp_reachable BOOLEAN, in_timeout BOOLEAN, in_session BOOLEAN, scoring_failures INT, scoring_failures_offset INT, scoring_timeout_occurrences INT, is_network_testing BOOLEAN) VOLATILE AS $$ +BEGIN + RETURN QUERY + SELECT SUM(CASE WHEN tmp.test_client_id IS NULL OR tmp.in_session OR tmp.in_timeout OR tmp.udp_reachable = FALSE OR tmp.is_network_testing = TRUE THEN 0 ELSE 1 END) AS work_count, tmp.client_id AS client_id, users.email, users.first_name, users.last_name, users.id AS user_id, tmp.udp_reachable, tmp.in_timeout, tmp.in_session, tmp.scoring_failures, tmp.scoring_failures_offset, tmp.scoring_timeout_occurrences, tmp.is_network_testing FROM + (SELECT connections.client_type, scorable_locations.client_id AS test_client_id, connections.client_id AS client_id, connections.user_id AS user_id, connections.udp_reachable, connections.scoring_timeout > NOW() as in_timeout, connections.music_session_id IS NOT NULL AS in_session, connections.scoring_failures, connections.scoring_failures_offset, connections.scoring_timeout_occurrences, connections.is_network_testing, scorable_locations.client_id IS NULL AS same_client, row_number() OVER (PARTITION BY connections.locidispid) AS rownum FROM connections LEFT OUTER JOIN scorable_locations(connections.client_id, connections.locidispid, connections.addr, stale_score) + ON connections.locidispid != scorable_locations.locidispid) tmp INNER JOIN users ON tmp.user_id = users.id WHERE tmp.client_type = 'client' GROUP BY tmp.client_id, users.email, users.first_name, users.last_name, users.id, tmp.same_client, tmp.udp_reachable, tmp.in_timeout, tmp.in_session, tmp.scoring_failures, tmp.scoring_failures_offset, tmp.scoring_timeout_occurrences, tmp.is_network_testing ORDER BY work_count DESC; + RETURN; +END; +$$ LANGUAGE plpgsql; + +DROP FUNCTION IF EXISTS get_work_summary_no_agg(stale_score INTERVAL); +-- useful for debugging get_work_summary +CREATE FUNCTION get_work_summary_no_agg (stale_score INTERVAL) RETURNS TABLE (client_id VARCHAR(64), test_client_id VARCHAR(64), email VARCHAR, first_name VARCHAR, last_name VARCHAR, user_id VARCHAR(64), udp_reachable BOOLEAN, in_timeout BOOLEAN, scoring_failures INT, scoring_failures_offset INT, scoring_timeout_occurrences INT, is_network_testing BOOLEAN) VOLATILE AS $$ +BEGIN + RETURN QUERY + SELECT tmp.client_id AS client_id, tmp.test_client_id, users.email, users.first_name, users.last_name, users.id AS user_id, tmp.udp_reachable, tmp.in_timeout, tmp.scoring_failures, tmp.scoring_failures_offset, tmp.scoring_timeout_occurrences, tmp.is_network_testing FROM + (SELECT scorable_locations.client_id AS test_client_id, connections.client_id AS client_id, connections.user_id AS user_id, connections.udp_reachable, connections.scoring_timeout > NOW() as in_timeout, connections.scoring_failures, connections.scoring_failures_offset, connections.scoring_timeout_occurrences, connections.is_network_testing, scorable_locations.client_id IS NULL AS same_client, row_number() OVER (PARTITION BY connections.locidispid) AS rownum FROM connections LEFT OUTER JOIN scorable_locations(connections.client_id, connections.locidispid, connections.addr, stale_score) + ON connections.locidispid != scorable_locations.locidispid AND connections.client_type = 'client') tmp INNER JOIN users ON tmp.user_id = users.id ORDER BY tmp.client_id; + RETURN; +END; +$$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/ruby/lib/jam_ruby/connection_manager.rb b/ruby/lib/jam_ruby/connection_manager.rb index e8349c97d..9033bbe66 100644 --- a/ruby/lib/jam_ruby/connection_manager.rb +++ b/ruby/lib/jam_ruby/connection_manager.rb @@ -89,7 +89,7 @@ module JamRuby udp_reachable_value = udp_reachable.nil? ? 'udp_reachable' : udp_reachable sql =< "JamRuby::Track", :inverse_of => :connection, :foreign_key => 'connection_id', :dependent => :delete_all - validates :as_musician, :inclusion => {:in => [true, false]} + validates :as_musician, :inclusion => {:in => [true, false, nil]} validates :client_type, :inclusion => {:in => [TYPE_CLIENT, TYPE_BROWSER, TYPE_LATENCY_TESTER]} validates_numericality_of :last_jam_audio_latency, greater_than:0, :allow_nil => true validate :can_join_music_session, :if => :joining_session? diff --git a/ruby/lib/jam_ruby/models/get_work.rb b/ruby/lib/jam_ruby/models/get_work.rb index 8946770fd..df5003925 100644 --- a/ruby/lib/jam_ruby/models/get_work.rb +++ b/ruby/lib/jam_ruby/models/get_work.rb @@ -12,6 +12,7 @@ module JamRuby def self.get_work_list(connection, rows = 25, staleness_hours = 120) + return [] if connection.is_network_testing # short-circuit 0 results if is_network_testing return [] unless connection.udp_reachable # short-circuit 0 results if udp_reachable return [] if connection.scoring_timeout > Time.now # short-circuit 0 results if in scoring timeout return [] if connection.in_session? @@ -25,7 +26,7 @@ module JamRuby end def self.summary(staleness_hours = 120) - r = GetWork.select([:work_count, :client_id, :email, :first_name, :last_name, :user_id, :udp_reachable, :in_timeout, :in_session, :scoring_failures, :scoring_failures_offset, :scoring_timeout_occurrences]).find_by_sql("select work_count, client_id, email, first_name, last_name, user_id, udp_reachable, in_timeout, in_session, scoring_failures, scoring_failures_offset, scoring_timeout_occurrences FROM get_work_summary(INTERVAL '#{staleness_hours} hours')" ) + r = GetWork.select([:work_count, :client_id, :email, :first_name, :last_name, :user_id, :udp_reachable, :in_timeout, :in_session, :scoring_failures, :scoring_failures_offset, :scoring_timeout_occurrences, :is_network_testing]).find_by_sql("select work_count, client_id, email, first_name, last_name, user_id, udp_reachable, in_timeout, in_session, scoring_failures, scoring_failures_offset, scoring_timeout_occurrences, is_network_testing FROM get_work_summary(INTERVAL '#{staleness_hours} hours')" ) end end end diff --git a/ruby/lib/jam_ruby/models/user.rb b/ruby/lib/jam_ruby/models/user.rb index 9cdc68a1c..06d19d5a5 100644 --- a/ruby/lib/jam_ruby/models/user.rb +++ b/ruby/lib/jam_ruby/models/user.rb @@ -1271,8 +1271,7 @@ module JamRuby if audio_latency > 2 # updating the connection is best effort if connection - connection.last_jam_audio_latency = audio_latency - connection.save + Connection.where(:id => connection.id).update_all(:last_jam_audio_latency => audio_latency) end self.last_jam_audio_latency = audio_latency diff --git a/ruby/spec/jam_ruby/models/get_work_spec.rb b/ruby/spec/jam_ruby/models/get_work_spec.rb index b47b7041d..e05c6cb95 100644 --- a/ruby/spec/jam_ruby/models/get_work_spec.rb +++ b/ruby/spec/jam_ruby/models/get_work_spec.rb @@ -176,6 +176,16 @@ describe GetWork do GetWork.get_work_list(other_connection2).should == [my_connection.client_id] end + it "excludes network testing clients" do + my_connection = FactoryGirl.create(:connection, locidispid: austin_geo[:locidispid], addr: 1) + other_connection = FactoryGirl.create(:connection, locidispid: dallas_geo[:locidispid], addr: 2, is_network_testing: true) + other_connection2 = FactoryGirl.create(:connection, locidispid: houston_geoip[:locidispid], addr: 3) + + GetWork.get_work_list(my_connection).should == [other_connection2.client_id] + GetWork.get_work_list(other_connection).should == [] + GetWork.get_work_list(other_connection2).should == [my_connection.client_id] + end + it "excludes scoring_timeout clients (1)" do my_connection = FactoryGirl.create(:connection, locidispid: austin_geo[:locidispid], addr: 1) other_connection = FactoryGirl.create(:connection, locidispid: dallas_geo[:locidispid], addr: 2, scoring_timeout: 1.days.from_now) diff --git a/web/app/assets/javascripts/dialog/networkTestDialog.js b/web/app/assets/javascripts/dialog/networkTestDialog.js index 5d64ceb64..da0d7130e 100644 --- a/web/app/assets/javascripts/dialog/networkTestDialog.js +++ b/web/app/assets/javascripts/dialog/networkTestDialog.js @@ -54,13 +54,14 @@ } function beforeShow() { + networkTest.haltScoring(); if(!networkTest.isScoring()) { networkTest.reset(); } } function afterHide() { - + networkTest.resumeScoring(); } function initialize() { diff --git a/web/app/assets/javascripts/everywhere/everywhere.js b/web/app/assets/javascripts/everywhere/everywhere.js index e90049966..71fd1169a 100644 --- a/web/app/assets/javascripts/everywhere/everywhere.js +++ b/web/app/assets/javascripts/everywhere/everywhere.js @@ -140,6 +140,9 @@ } function updateScoringIntervals() { + // make sure latency testing is still going on, in case a refresh occurred during network test + context.jamClient.SetLatencyTestBlocked(false) + // set scoring intervals if(context.jamClient.SetScoreWorkTimingInterval){ var success = context.jamClient.SetScoreWorkTimingInterval( diff --git a/web/app/assets/javascripts/fakeJamClient.js b/web/app/assets/javascripts/fakeJamClient.js index f6af7789f..393501e1b 100644 --- a/web/app/assets/javascripts/fakeJamClient.js +++ b/web/app/assets/javascripts/fakeJamClient.js @@ -362,6 +362,18 @@ return 8; } + function SetLatencyTestBlocked(blocked) { + + } + + function isLatencyTestBlocked() { + return false; + } + + function GetLastLatencyTestTimes() { + return { initiated: 10000, requested: 10000} + } + function GetASIODevices() { var response =[{"device_id":0,"device_name":"Realtek High Definition Audio","device_type": 0,"interfaces":[{"interface_id":0,"interface_name":"Realtek HDA SPDIF Out","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":1,"interface_name":"Realtek HD Audio rear output","pins":[{"is_input":false,"pin_id":0,"pin_name":"PC Speaker"}]},{"interface_id":2,"interface_name":"Realtek HD Audio Mic input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":3,"interface_name":"Realtek HD Audio Line input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]},{"interface_id":4,"interface_name":"Realtek HD Digital input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"}]},{"interface_id":5,"interface_name":"Realtek HD Audio Stereo input","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"}]}],"wavert_supported":false},{"device_id":1,"device_name":"M-Audio FW Audiophile","device_type": 1,"interfaces":[{"interface_id":0,"interface_name":"FW AP Multi","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":1,"interface_name":"FW AP 1/2","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":2,"interface_name":"FW AP SPDIF","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"},{"is_input":true,"pin_id":1,"pin_name":"Input"}]},{"interface_id":3,"interface_name":"FW AP 3/4","pins":[{"is_input":false,"pin_id":0,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":2,"device_name":"Virtual Audio Cable","device_type": 2,"interfaces":[{"interface_id":0,"interface_name":"Virtual Cable 2","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]},{"interface_id":1,"interface_name":"Virtual Cable 1","pins":[{"is_input":true,"pin_id":0,"pin_name":"Capture"},{"is_input":false,"pin_id":1,"pin_name":"Output"}]}],"wavert_supported":false},{"device_id":3,"device_name":"WebCamDV WDM Audio Capture","device_type": 3,"interfaces":[{"interface_id":0,"interface_name":"WebCamDV Audio","pins":[{"is_input":true,"pin_id":0,"pin_name":"Recording Control"},{"is_input":false,"pin_id":1,"pin_name":"Volume Control"}]}],"wavert_supported":false}]; return response; @@ -823,6 +835,9 @@ this.IsMyNetworkWireless = IsMyNetworkWireless; this.SetNetworkTestScore = SetNetworkTestScore; this.GetNetworkTestScore = GetNetworkTestScore; + this.SetLatencyTestBlocked = SetLatencyTestBlocked; + this.isLatencyTestBlocked = isLatencyTestBlocked; + this.GetLastLatencyTestTimes = GetLastLatencyTestTimes; this.RegisterQuitCallback = RegisterQuitCallback; this.LeaveSessionAndMinimize = LeaveSessionAndMinimize; this.GetAutoStart = GetAutoStart; diff --git a/web/app/assets/javascripts/jam_rest.js b/web/app/assets/javascripts/jam_rest.js index 3c1e95027..ff031af99 100644 --- a/web/app/assets/javascripts/jam_rest.js +++ b/web/app/assets/javascripts/jam_rest.js @@ -503,6 +503,19 @@ }); } + function updateNetworkTesting(options) { + var id = getId(options); + + return $.ajax({ + type: "POST", + dataType: "json", + contentType: 'application/json', + url: "/api/users/" + id + "/is_network_testing", + data: JSON.stringify(options), + processData: false + }); + } + function updateAvatar(options) { var id = getId(options); @@ -1222,6 +1235,7 @@ this.getInstruments = getInstruments; this.getGenres = getGenres; this.updateUdpReachable = updateUdpReachable; + this.updateNetworkTesting = updateNetworkTesting; this.updateAvatar = updateAvatar; this.deleteAvatar = deleteAvatar; this.getFilepickerPolicy = getFilepickerPolicy; diff --git a/web/app/assets/javascripts/networkTestHelper.js b/web/app/assets/javascripts/networkTestHelper.js index 0e6d3add8..601ecd860 100644 --- a/web/app/assets/javascripts/networkTestHelper.js +++ b/web/app/assets/javascripts/networkTestHelper.js @@ -167,6 +167,35 @@ return lastNetworkFailure; } + function haltScoring() { + context.jamClient.SetLatencyTestBlocked(true) + rest.updateNetworkTesting({client_id: app.clientId, is_network_testing: true}) + .fail(function(jqXHR) { + + if(jqXHR.status == 404) { + // assume connection is missing + app.notifyAlert("Not Connected", "You must be connected to the server to run the network test.") + } + else { + app.notifyServerError(jqXHR, "Unable to tell server that we are beginning the network test") + } + }) + } + + function resumeScoring() { + context.jamClient.SetLatencyTestBlocked(false) + rest.updateNetworkTesting({client_id: app.clientId, is_network_testing: false}) + .fail(function(jqXHR) { + if(jqXHR.status == 404) { + // assume connection is missing + // do nothing in this case + } + else { + app.notifyServerError(jqXHR, "Unable to tell server that we are ending the network test") + } + }) + } + function storeLastNetworkFailure(reason, data) { if (!trackedPass) { lastNetworkFailure = {reason: reason, data: data}; @@ -471,63 +500,125 @@ } } + function pauseForRecentScoresTime() { + var lastScoreTimes = context.jamClient.GetLastLatencyTestTimes() + + console.log(lastScoreTimes) + + return 0; + + var noPause = 0; + var longAgo = 1000000; + var initiated = lastScoreTimes.initiatied; + var requested = lastScoreTimes.requested; + + if(initiated === null || initiated === undefined) { + logger.warn("lastScoreTimes.initiated is not set"); + initiated = longAgo; + } + if(requested === null || requested === undefined) { + logger.warn("lastScoreTimes.requested is not set"); + requested = longAgo; + } + + if(initiated == 0) { + logger.debug("lastScoreTimes.initiated is zero"); + initiated = longAgo; + } + if(requested == 0) { + logger.debug("lastScoreTimes.requested is zero"); + requested = longAgo; + } + + if(initiated < 0) { + logger.debug("lastScoreTimes.initiated is less than zero"); + initiated = longAgo; + } + if(requested < 0) { + logger.debug("lastScoreTimes.requested is less than zero"); + requested = longAgo; + } + + var mostRecentValue = initiated < requested ? initiated : requested; + + if(mostRecentValue > gon.globalftue_network_test_min_wait_since_last_score * 1000) { + return noPause; // our last score was past our min wait; so no delay necessary + } + else { + // pause for the remainder of the min wait threshold + var remainder = gon.globalftue_network_test_min_wait_since_last_score * 1000 - mostRecentValue; + + if(remainder > 1500) { + // we need to update the UI because this is a long time for a mystery pause + $startNetworkTestBtn.text('SHORT QUIET PERIOD...') + } + + return remainder; + } + } function prepareNetworkTest() { if (scoring) return false; - logger.info("starting network test"); - resetTestState(); - scoring = true; - $self.triggerHandler(NETWORK_TEST_START); - renderStartTest(); - rest.getLatencyTester() - .done(function (response) { - // ensure there are no tests ongoing - serverClientId = response.client_id; - testSummary.serverClientId = serverClientId; + setTimeout(function() { - logger.info("beginning network test against client_id: " + serverClientId); + logger.info("starting network test"); + resetTestState(); + scoring = true; + $self.triggerHandler(NETWORK_TEST_START); + renderStartTest(); + rest.getLatencyTester() + .done(function (response) { + // ensure there are no tests ongoing - primePump() - .done(function () { - postPumpRun(); - }) - .fail(function () { - logger.debug("unable to determine user's network type. primePump failed.") - context.JK.Banner.showAlert({ - title: 'Unable to Determine Network Type', - buttons: [ - {name: 'CANCEL', click: function () { - cancelTest(); - }}, - {name: 'RUN NETWORK TEST ANYWAY', click: function () { - attemptTestPass(); - ; - }} - ], - html: "

We are unable to determine if your computer is connected to your network using WiFi.

" + - "

We strongly advise against running the JamKazam application on a WiFi connection. " + - "We recommend using a wired Ethernet connection from your computer to your router. " + - "A WiFi connection is likely to cause significant issues in both latency and audio quality.

"}) - }); - }) - .fail(function (jqXHR) { - if (jqXHR.status == 404) { - // means there are no network testers available. - // we have to skip this part of the UI - testSummary.final = {reason: 'no_servers'} - } - else { - if (context.JK.isNetworkError(arguments)) { - testSummary.final = {reason: 'no_network'} + serverClientId = response.client_id; + + testSummary.serverClientId = serverClientId; + + logger.info("beginning network test against client_id: " + serverClientId); + + primePump() + .done(function () { + postPumpRun(); + }) + .fail(function () { + logger.debug("unable to determine user's network type. primePump failed.") + context.JK.Banner.showAlert({ + title: 'Unable to Determine Network Type', + buttons: [ + {name: 'CANCEL', click: function () { + cancelTest(); + }}, + {name: 'RUN NETWORK TEST ANYWAY', click: function () { + attemptTestPass(); + ; + }} + ], + html: "

We are unable to determine if your computer is connected to your network using WiFi.

" + + "

We strongly advise against running the JamKazam application on a WiFi connection. " + + "We recommend using a wired Ethernet connection from your computer to your router. " + + "A WiFi connection is likely to cause significant issues in both latency and audio quality.

"}) + }); + }) + .fail(function (jqXHR) { + if (jqXHR.status == 404) { + // means there are no network testers available. + // we have to skip this part of the UI + testSummary.final = {reason: 'no_servers'} } else { - testSummary.final = {reason: 'rest_api_error'} + if (context.JK.isNetworkError(arguments)) { + testSummary.final = {reason: 'no_network'} + } + else { + testSummary.final = {reason: 'rest_api_error'} + } } - } - testFinished(); - }) + testFinished(); + }) + }, pauseForRecentScoresTime()) + return false; } @@ -844,6 +935,8 @@ this.reset = reset; this.cancel = cancel; this.getLastNetworkFailure = getLastNetworkFailure; + this.haltScoring = haltScoring; + this.resumeScoring = resumeScoring; this.NETWORK_TEST_START = NETWORK_TEST_START; this.NETWORK_TEST_DONE = NETWORK_TEST_DONE; diff --git a/web/app/assets/javascripts/wizard/gear/step_network_test.js b/web/app/assets/javascripts/wizard/gear/step_network_test.js index b82a622ae..17f28d06a 100644 --- a/web/app/assets/javascripts/wizard/gear/step_network_test.js +++ b/web/app/assets/javascripts/wizard/gear/step_network_test.js @@ -53,11 +53,13 @@ } function beforeShow() { + networkTest.haltScoring(); networkTest.cancel(); updateButtons(); } function beforeHide() { + networkTest.resumeScoring(); networkTest.cancel(); } diff --git a/web/app/controllers/api_users_controller.rb b/web/app/controllers/api_users_controller.rb index ddfcf4630..d296e334b 100644 --- a/web/app/controllers/api_users_controller.rb +++ b/web/app/controllers/api_users_controller.rb @@ -669,8 +669,17 @@ class ApiUsersController < ApiController def udp_reachable Connection.transaction do @connection = Connection.find_by_client_id!(params[:client_id]) - @connection.udp_reachable = params[:udp_reachable] - @connection.save + # deliberately don't updated_at on connection! only heartbeats do that + Connection.where(:id => @connection.id).update_all(:udp_reachable => params[:udp_reachable]) + respond_with_model(@connection) + end + end + + def is_network_testing + Connection.transaction do + @connection = Connection.find_by_client_id!(params[:client_id]) + # deliberately don't updated_at on connection! only heartbeats do that + Connection.where(:id => @connection.id).update_all(:is_network_testing => params[:is_network_testing]) respond_with_model(@connection) end end diff --git a/web/config/application.rb b/web/config/application.rb index 575654d27..6947079af 100644 --- a/web/config/application.rb +++ b/web/config/application.rb @@ -248,6 +248,8 @@ if defined?(Bundler) config.ftue_network_test_packet_size = 60 # number of times that the backend retries before giving up config.ftue_network_test_backend_retries = 10 + # amount of time that we want passed until we run the next network test + config.ftue_network_test_min_wait_since_last_score = 5 # the maximum amount of allowable latency config.ftue_maximum_gear_latency = 20 diff --git a/web/config/initializers/gon.rb b/web/config/initializers/gon.rb index e2b1feb79..567b1e8e0 100644 --- a/web/config/initializers/gon.rb +++ b/web/config/initializers/gon.rb @@ -4,4 +4,5 @@ Gon.global.ftue_network_test_backend_retries = Rails.application.config.ftue_net Gon.global.twitter_public_account = Rails.application.config.twitter_public_account Gon.global.scoring_get_work_interval = Rails.application.config.scoring_get_work_interval Gon.global.scoring_get_work_backoff_interval = Rails.application.config.scoring_get_work_backoff_interval +Gon.global.ftue_network_test_min_wait_since_last_score = Rails.application.config.ftue_network_test_min_wait_since_last_score Gon.global.env = Rails.env diff --git a/web/config/routes.rb b/web/config/routes.rb index 723298515..5bdab1017 100644 --- a/web/config/routes.rb +++ b/web/config/routes.rb @@ -297,6 +297,7 @@ SampleApp::Application.routes.draw do # udp reachable (can stun?) match '/users/:id/udp_reachable' => 'api_users#udp_reachable', :via => :post + match '/users/:id/is_network_testing' => 'api_users#is_network_testing', :via => :post # social match '/users/:id/share/session/:provider' => 'api_users#share_session', :via => :get