diff --git a/ruby/lib/jam_ruby/lib/em_helper.rb b/ruby/lib/jam_ruby/lib/em_helper.rb index ba6e582e8..5867424ef 100644 --- a/ruby/lib/jam_ruby/lib/em_helper.rb +++ b/ruby/lib/jam_ruby/lib/em_helper.rb @@ -13,11 +13,15 @@ module JamWebEventMachine @@log = Logging.logger[JamWebEventMachine] + + # THIS WAS USED BY resque jobs needing EventMachine/AMQP, but it's no longer needed. It's useful code though + # starts amqp & eventmachine up first. # and then calls your block. # After the supplied block is done, # waits until all EM tasks scheduled in the supplied block are done, or timeout def self.run_wait_stop(timeout = 30, &blk) + JamWebEventMachine.run thread = Thread.current diff --git a/ruby/lib/jam_ruby/lib/stats.rb b/ruby/lib/jam_ruby/lib/stats.rb index bb73931f5..c51a14ae1 100644 --- a/ruby/lib/jam_ruby/lib/stats.rb +++ b/ruby/lib/jam_ruby/lib/stats.rb @@ -1,5 +1,41 @@ + require 'influxdb' +# monkey patch InfluxDB client to clear the queue when asked to stop +module InfluxDB + class Client + def stop! + @queue.clear if @queue + @stopped = true + end + end +end + +module InfluxDB + class Worker + def spawn_threads! + NUM_WORKER_THREADS.times do |thread_num| + log :debug, "Spawning background worker thread #{thread_num}." + + Thread.new do + Thread.current[:influxdb] = self.object_id + + at_exit do + log :debug, "Thread exiting, bailing out (not flushing queue)" + end + + while !client.stopped? + self.check_background_queue(thread_num) + sleep rand(SLEEP_INTERVAL) + end + end + end + + end + + end +end + module JamRuby class Stats @@ -10,6 +46,7 @@ module JamRuby def self.destroy! if @client + @client.queue.clear if @client.queue @client.stop! end end diff --git a/ruby/lib/jam_ruby/resque/audiomixer.rb b/ruby/lib/jam_ruby/resque/audiomixer.rb index 7ea7e7e55..cbe62d92e 100644 --- a/ruby/lib/jam_ruby/resque/audiomixer.rb +++ b/ruby/lib/jam_ruby/resque/audiomixer.rb @@ -20,13 +20,11 @@ module JamRuby def self.perform(mix_id, postback_ogg_url, postback_mp3_url) - JamWebEventMachine.run_wait_stop do - audiomixer = AudioMixer.new() - audiomixer.postback_ogg_url = postback_ogg_url - audiomixer.postback_mp3_url = postback_mp3_url - audiomixer.mix_id = mix_id - audiomixer.run - end + audiomixer = AudioMixer.new() + audiomixer.postback_ogg_url = postback_ogg_url + audiomixer.postback_mp3_url = postback_mp3_url + audiomixer.mix_id = mix_id + audiomixer.run end diff --git a/ruby/lib/jam_ruby/resque/quick_mixer.rb b/ruby/lib/jam_ruby/resque/quick_mixer.rb index daddc1802..93704173f 100644 --- a/ruby/lib/jam_ruby/resque/quick_mixer.rb +++ b/ruby/lib/jam_ruby/resque/quick_mixer.rb @@ -19,12 +19,10 @@ module JamRuby def self.perform(quick_mix_id, postback_mp3_url) - JamWebEventMachine.run_wait_stop do - audiomixer = QuickMixer.new - audiomixer.postback_mp3_url = postback_mp3_url - audiomixer.quick_mix_id = quick_mix_id - audiomixer.run - end + audiomixer = QuickMixer.new + audiomixer.postback_mp3_url = postback_mp3_url + audiomixer.quick_mix_id = quick_mix_id + audiomixer.run end diff --git a/ruby/lib/jam_ruby/resque/resque_hooks.rb b/ruby/lib/jam_ruby/resque/resque_hooks.rb index 37685f0d8..9b27e7097 100644 --- a/ruby/lib/jam_ruby/resque/resque_hooks.rb +++ b/ruby/lib/jam_ruby/resque/resque_hooks.rb @@ -1,26 +1,53 @@ require 'resque' +require 'resque-lonely_job' -# https://devcenter.heroku.com/articles/forked-pg-connections -Resque.before_fork do - defined?(ActiveRecord::Base) and - ActiveRecord::Base.connection.disconnect! +ENV['FORK_PER_JOB'] = 'false' - JamRuby::Stats.destroy! +def shutdown + puts "Cleaning up resources..." + Stats.destroy! + EventMachine.stop_event_loop + puts "Terminated!" + exit! end -Resque.after_fork do - defined?(ActiveRecord::Base) and - ActiveRecord::Base.establish_connection +Resque.before_first_fork do + JamWebEventMachine.start + #ActiveRecord::Base.establish_connection config = { influxdb_database: APP_CONFIG.influxdb_database, influxdb_username: APP_CONFIG.influxdb_username, influxdb_password: APP_CONFIG.influxdb_password, influxdb_hosts: APP_CONFIG.influxdb_hosts, influxdb_port: APP_CONFIG.influxdb_port, - influxdb_async: false # if we use async=true, the forked job will die before the stat is sent + influxdb_async: true # if we use async=true, the forked job will die before the stat is sent } + + # handle these events and force a shutdown. this is required I think due to influxdb-client. + Signal.trap("TERM") do + shutdown + end + Signal.trap("INT") do + shutdown + end + JamRuby::Stats.init(config) + +end +# https://devcenter.heroku.com/articles/forked-pg-connections +Resque.before_fork do + + #defined?(ActiveRecord::Base) and + # ActiveRecord::Base.connection.disconnect! + + #JamRuby::Stats.destroy! +end + +Resque.after_fork do + #defined?(ActiveRecord::Base) and + # ActiveRecord::Base.establish_connection + end # for jobs that do not extend lonely job, just extend this module and get stats diff --git a/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb index e700a0427..0b12cc0ce 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/active_music_session_cleaner.rb @@ -22,11 +22,9 @@ module JamRuby def self.perform @@log.debug("ActiveMusicSessionCleaner waking up") - JamWebEventMachine.run_wait_stop do - cleaner = ActiveMusicSessionCleaner.new - cleaner.interval = "INTERVAL '1 minute'" - cleaner.run - end + cleaner = ActiveMusicSessionCleaner.new + cleaner.interval = "INTERVAL '1 minute'" + cleaner.run @@log.debug("ActiveMusicSessionCleaner done") end diff --git a/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb b/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb index b81c5b992..6b18cb633 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/icecast_source_check.rb @@ -23,9 +23,7 @@ module JamRuby def self.perform @@log.debug("waking up") - JamWebEventMachine.run_wait_stop do IcecastSourceCheck.new.run - end @@log.debug("done") end diff --git a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb index cd706285a..6fa751ef9 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/music_session_scheduler.rb @@ -19,9 +19,7 @@ module JamRuby def self.perform @@log.debug("MusicSessionScheduler waking up") - JamWebEventMachine.run_wait_stop do - MusicSessionScheduler.new.run - end + MusicSessionScheduler.new.run @@log.debug("MusicSessionScheduler done") end diff --git a/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb b/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb index 9fb3e307f..5955b1ec3 100644 --- a/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb +++ b/ruby/lib/jam_ruby/resque/scheduled/unused_music_notation_cleaner.rb @@ -20,9 +20,7 @@ module JamRuby def self.perform @@log.debug("waking up") - JamWebEventMachine.run_wait_stop do - UnusedMusicNotationCleaner.new.run - end + UnusedMusicNotationCleaner.new.run @@log.debug("done") end diff --git a/ruby/spec/spec_helper.rb b/ruby/spec/spec_helper.rb index 73e9a3384..890a195c0 100644 --- a/ruby/spec/spec_helper.rb +++ b/ruby/spec/spec_helper.rb @@ -12,6 +12,10 @@ require 'uses_temp_files' require 'resque_spec' require 'resque_failed_job_mailer' +# to prevent embedded resque code from forking +ENV['FORK_PER_JOB'] = 'false' + + # recreate test database and migrate it SpecDb::recreate_database diff --git a/web/app/assets/javascripts/jquery.listenbroadcast.js b/web/app/assets/javascripts/jquery.listenbroadcast.js index 349305b57..66c20d850 100644 --- a/web/app/assets/javascripts/jquery.listenbroadcast.js +++ b/web/app/assets/javascripts/jquery.listenbroadcast.js @@ -81,7 +81,7 @@ if($audio.length == 0) { $audio = $('') $parent.append($audio) audioDomElement = $audio.get(0); @@ -105,7 +105,7 @@ if(!response.mount) { transition(PlayStateSessionOver); destroy(); - } + } else { audioDomElement.play(); @@ -164,9 +164,15 @@ audioDomElement.load(); var $parent = $audio.parent(); $audio.remove(); - $parent.append(''); - $audio = $('audio', $parent); + $audio = $('') $audio.append(originalSource); + var $sources = $audio.find('source') + $.each($sources, function(i, source) { + var $source = $(source); + var bustedSource = cacheBustedSrc($source.attr('data-audio-src')) + $source.attr('src', bustedSource) + }) + $parent.append($audio); audioDomElement = $audio.get(0); audioBind(); logger.log("recreated audio element ") @@ -225,6 +231,9 @@ else { // tell audio to stop/start, in attempt to retry //audioDomElement.pause(); + + recreateAudioElement(); + audioDomElement.load(); if(needsCanPlayGuard()) { $audio.bind('canplay', function() { @@ -443,7 +452,12 @@ // we have cause to believe the session is done; check against the server if(refresh) { - checkServer(); + checkServer() + .done(function(response) { + if(!response.mount) { + transition(PlayStateSessionOver); + destroy(); + }}) } } @@ -634,6 +648,7 @@ function openBubble() { checkServer().done(function(response) { + var mountId = response.mount ? response.mount.id : null if(mountId) { @@ -648,6 +663,10 @@ } else { mountInfo = null; + + transition(PlayStateSessionOver); + destroy(); + context.JK.app.layout.notify('This session can not currently broadcast') } }) @@ -679,6 +698,11 @@ }) } } + + function cacheBustedSrc(src) { + return src + '?cache-buster=' + new Date().getTime(); + } + function initialize() { @@ -690,6 +714,7 @@ fanAccess = $parent.attr('fan-access') === 'true' // coerce to boolean if(lazyAudioInit) { + // save the original src element (without any cache bust) audioSrc = $parent.attr('data-audio-src'); if(audioSrc === null) throw 'data-audio-src must be specified in $parentElement'; audioType = $parent.attr('data-audio-type'); @@ -709,7 +734,17 @@ } if(!lazyAudioInit) { + var $sources = $audio.find('source') + $.each($sources, function(i, source) { + var $source = $(source); + // save original src value (before cache bust) + $source.attr('data-audio-src', $source.attr('src')) + + var bustedSource = cacheBustedSrc($source.attr('data-audio-src')) + $source.attr('src', bustedSource) + }) audioDomElement = $audio.get(0); + audioBind(); } diff --git a/web/app/assets/javascripts/sessionList.js b/web/app/assets/javascripts/sessionList.js index 80507b011..46f32fcad 100644 --- a/web/app/assets/javascripts/sessionList.js +++ b/web/app/assets/javascripts/sessionList.js @@ -40,7 +40,7 @@ if(data.isEnd) { $listenText.text('Listen').removeClass('statusing') - stopPlay(); + stopPlay($listenLink); } if(data.isSessionOver) { @@ -60,8 +60,9 @@ function togglePlay() { var $listenLink = $(this) - var $listenText = $('.listen-link-text', $listenLink); - var $listenDetails = $('.listen-link-details', $listenLink); + var $parent = $listenLink.closest('.action-links'); + var $listenText = $('.listen-link-text', $parent); + var $listenDetails = $('.listen-link-details', $parent); if($listenLink.data('listenbroadcast-playstate') == 'playing') { $listenText.text('Listen') $listenDetails.removeClass('statusing') diff --git a/web/app/assets/javascripts/web/sessions.js b/web/app/assets/javascripts/web/sessions.js index 397795585..b39e16686 100644 --- a/web/app/assets/javascripts/web/sessions.js +++ b/web/app/assets/javascripts/web/sessions.js @@ -106,7 +106,7 @@ $controls.bind('statechange.listenBroadcast', stateChange); context.JK.prettyPrintElements($('time.duration').show()); context.JK.TickDuration(null); - $playButton.click(togglePlay); + $playButton.click(startPlay); sessionId = musicSessionId; diff --git a/web/config/environment.rb b/web/config/environment.rb index 3807f21a1..3463d46ae 100644 --- a/web/config/environment.rb +++ b/web/config/environment.rb @@ -3,10 +3,14 @@ require File.expand_path('../application', __FILE__) Mime::Type.register "audio/ogg", :audio_ogg +# to prevent embedded resque code from forking +ENV['FORK_PER_JOB'] = 'false' + # assign globals APP_CONFIG = Rails.application.config Stats.client = InfluxDB::Rails.client + # Initialize the rails application SampleApp::Application.initialize! diff --git a/web/lib/tasks/start.rake b/web/lib/tasks/start.rake index b53b25f6c..2efd63076 100644 --- a/web/lib/tasks/start.rake +++ b/web/lib/tasks/start.rake @@ -4,6 +4,7 @@ task :all_jobs do Rake::Task['environment'].invoke + ENV['FORK_PER_JOB'] = 'false' ENV['QUEUE'] = ENV['QUEUE'] || '*' Rake::Task['resque:work'].invoke end @@ -12,6 +13,7 @@ end task :audiomixer do Rake::Task['environment'].invoke + ENV['FORK_PER_JOB'] = 'false' ENV['QUEUE'] = 'audiomixer' Rake::Task['resque:work'].invoke end @@ -20,6 +22,7 @@ end task :icecast do Rake::Task['environment'].invoke + ENV['FORK_PER_JOB'] = 'false' ENV['QUEUE'] = 'icecast' Rake::Task['resque:work'].invoke end @@ -29,6 +32,7 @@ end task :odd_jobs do Rake::Task['environment'].invoke + ENV['FORK_PER_JOB'] = 'false' ENV['QUEUE'] = '*,!icecast,!audiomixer' Rake::Task['resque:work'].invoke end diff --git a/web/spec/factories.rb b/web/spec/factories.rb index a1d3a1ef8..26756e206 100644 --- a/web/spec/factories.rb +++ b/web/spec/factories.rb @@ -86,7 +86,6 @@ FactoryGirl.define do before(:create) do |user, evaluator| if evaluator.specific_instruments evaluator.specific_instruments.each do |instrument| - puts "burp: " user.musician_instruments << FactoryGirl.build(:musician_instrument, user: user, instrument: instrument) end else diff --git a/web/spec/features/create_session_spec.rb b/web/spec/features/create_session_spec.rb index b431d8d25..8bcd553a7 100644 --- a/web/spec/features/create_session_spec.rb +++ b/web/spec/features/create_session_spec.rb @@ -149,7 +149,7 @@ describe "Create Session", :js => true, :type => :feature, :capybara_feature => btn = first('#btn-alert-ok')# accept the 'If you start this session now, the scheduled start time...' btn.trigger(:click) if btn - + expect(page).to have_selector('h2', text: 'my tracks') find('#session-screen .session-mytracks .session-track') end diff --git a/web/spec/spec_helper.rb b/web/spec/spec_helper.rb index 936471e11..09606e087 100644 --- a/web/spec/spec_helper.rb +++ b/web/spec/spec_helper.rb @@ -67,6 +67,9 @@ Thread.new { end } +# to prevent embedded resque code from forking +ENV['FORK_PER_JOB'] = 'false' + bputs "before load websocket server" current = Thread.current