This commit is contained in:
Steven Miers 2014-11-01 10:43:33 -05:00
parent 38cc681817
commit 0f1c436f60
22 changed files with 292 additions and 67 deletions

View File

@ -126,6 +126,8 @@ module JamAdmin
config.twitter_app_secret = ENV['TWITTER_APP_SECRET'] || 'Azcy3QqfzYzn2fsojFPYXcn72yfwa0vG6wWDrZ3KT8'
config.ffmpeg_path = ENV['FFMPEG_PATH'] || (File.exist?('/usr/local/bin/ffmpeg') ? '/usr/local/bin/ffmpeg' : '/usr/bin/ffmpeg')
config.normalize_ogg_path = ENV['NORMALIZE_OGG_PATH'] || (File.exist?('/usr/local/bin/normalize-ogg') ? '/usr/local/bin/normalize-ogg' : '/usr/bin/normalize-ogg')
config.normalize_mp3_path = ENV['NORMALIZE_MP3_PATH'] || (File.exist?('/usr/local/bin/normalize-mp3') ? '/usr/local/bin/normalize-mp3' : '/usr/bin/normalize-mp3')
config.max_audio_downloads = 100

View File

@ -481,6 +481,7 @@ message RecordingMasterMixComplete {
optional string msg = 3;
optional string notification_id = 4;
optional string created_at = 5;
optional string claimed_recording_id = 6;
}
message DownloadAvailable {

View File

@ -27,6 +27,10 @@ module JamRuby
"#{base_url}/findSession"
end
def self.session(session)
"#{base_url}/session/#{session.id}"
end
private
def self.base_url

View File

@ -712,9 +712,10 @@ module JamRuby
)
end
def recording_master_mix_complete(receiver_id, recording_id, band_id, msg, notification_id, created_at)
def recording_master_mix_complete(receiver_id, recording_id, claimed_recording_id, band_id, msg, notification_id, created_at)
recording_master_mix_complete = Jampb::RecordingMasterMixComplete.new(
:recording_id => recording_id,
:claimed_recording_id => claimed_recording_id,
:band_id => band_id,
:msg => msg,
:notification_id => notification_id,

View File

@ -1147,6 +1147,7 @@ module JamRuby
msg = @@message_factory.recording_master_mix_complete(
claimed_recording.user_id,
recording.id,
claimed_recording.id,
notification.band_id,
notification_msg,
notification.id,

View File

@ -322,7 +322,7 @@ module JamRuby
raise "no output ogg file after mix" unless File.exist? @output_ogg_filename
ffmpeg_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{@output_ogg_filename}\" -ab 128k -metadata JamRecordingId=#{@manifest[:recording_id]} -metadata JamMixId=#{@mix_id} -metadata JamType=Mix \"#{@output_mp3_filename}\""
ffmpeg_cmd = "#{APP_CONFIG.ffmpeg_path} -i \"#{@output_ogg_filename}\" -ab 192k -metadata JamRecordingId=#{@manifest[:recording_id]} -metadata JamMixId=#{@mix_id} -metadata JamType=Mix \"#{@output_mp3_filename}\""
system(ffmpeg_cmd)
@ -335,6 +335,30 @@ module JamRuby
end
raise "no output mp3 file after conversion" unless File.exist? @output_mp3_filename
# time to normalize both mp3 and ogg files
normalize_ogg_cmd = "#{APP_CONFIG.normalize_ogg_path} --bitrate 128 -i \"#{@output_ogg_filename}\""
system(normalize_ogg_cmd)
unless $? == 0
@error_reason = 'normalize-ogg-failed'
@error_detail = $?.to_s
error_msg = "normalize-ogg failed status=#{$?} error_reason=#{@error_reason} error_detail=#{@error_detail}"
@@log.info(error_msg)
raise error_msg
end
raise "no output ogg file after normalization" unless File.exist? @output_ogg_filename
normalize_mp3_cmd = "#{APP_CONFIG.normalize_mp3_path} --bitrate 128 -i \"#{@output_mp3_filename}\""
system(normalize_mp3_cmd)
unless $? == 0
@error_reason = 'normalize-mp3-failed'
@error_detail = $?.to_s
error_msg = "normalize-mp3 failed status=#{$?} error_reason=#{@error_reason} error_detail=#{@error_detail}"
@@log.info(error_msg)
raise error_msg
end
raise "no output mp3 file after conversion" unless File.exist? @output_mp3_filename
end
def symbolize_keys(obj)

View File

@ -38,6 +38,14 @@ def app_config
ENV['FFMPEG_PATH'] || '/usr/local/bin/ffmpeg'
end
def normalize_ogg_path
ENV['NORMALIZE_OGG_PATH'] || '/usr/local/bin/normalize-ogg'
end
def normalize_mp3_path
ENV['NORMALIZE_MP3_PATH'] || '/usr/local/bin/normalize-mp3'
end
def icecast_reload_cmd
'true' # as in, /bin/true
end

View File

@ -25,6 +25,7 @@
var notificationBatchSize = 20;
var currentNotificationPage = 0;
var didLoadAllNotifications = false, isLoading = false;
var ui = new context.JK.UIHelper(JK.app);
function isNotificationsPanelVisible() {
return $contents.is(':visible')
@ -1101,7 +1102,7 @@
"class": "button-orange",
callback: shareRecording,
callback_args: {
"recording_id": payload.recording_id
"claimed_recording_id": payload.claimed_recording_id
}
}]
);
@ -1109,7 +1110,9 @@
}
function shareRecording(args) {
var recordingId = args.recording_id;
var claimedRecordingId = args.claimed_recording_id;
ui.launchShareDialog(claimedRecordingId, 'recording');
}
function registerBandInvitation() {

View File

@ -84,17 +84,25 @@ class RecordingUtils
mixStateClass = 'discarded'
mixState = 'discarded'
else
mixStateMsg = 'STILL UPLOADING'
mixStateClass = 'still-uploading'
mixState = 'still-uploading'
return {
if mix.fake and mix.discarded
mixStateMsg = 'DISCARDED'
mixStateClass = 'discarded'
mixState = 'discarded'
else
mixStateMsg = 'STILL UPLOADING'
mixStateClass = 'still-uploading'
mixState = 'still-uploading'
result = {
mixStateMsg: mixStateMsg,
mixStateClass: mixStateClass,
mixState: mixState,
isError: mixState == 'error'
}
result
onMixHover: () ->
$mix = $(this).closest('.mix')
mixStateInfo = $mix.data('mix-state')
@ -114,7 +122,10 @@ class RecordingUtils
serverInfo = $mix.data('server-info')
# lie if this is a virtualized mix (i.e., mix is created after recording is made)
mixState = 'still-uploading' if !serverInfo? or serverInfo.fake
if !serverInfo?
mixState = 'still-uploading'
else if serverInfo.fake
mixState = if serverInfo.discarded then 'discarded' else 'still-uploading'
summary = ''
if mixState == 'still-uploading'

View File

@ -371,6 +371,14 @@
if(response["errors"] && response["errors"]["tracks"] && response["errors"]["tracks"][0] == "Please select at least one track") {
app.notifyAlert("No Inputs Configured", $('<span>You will need to reconfigure your audio device.</span>'));
}
else if(response["errors"] && response["errors"]["music_session"] && response["errors"]["music_session"][0] == ["is currently recording"]) {
promptLeave = false;
context.window.location = "/client#/findSession";
app.notify( { title: "Unable to Join Session", text: "The session is currently recording." }, null, true);
}
else {
app.notifyServerError(xhr, 'Unable to Join Session');
}
}
else {
app.notifyServerError(xhr, 'Unable to Join Session');
@ -396,7 +404,12 @@
if(screenActive) {
// this path is possible if FTUE is invoked on session page, and they cancel
sessionModel.leaveCurrentSession()
.fail(app.ajaxError);
.fail(function(jqXHR) {
if(jqXHR.status != 404) {
logger.debug("leave session failed");
app.ajaxError(arguments)
}
});
}
screenActive = false;

View File

@ -184,10 +184,10 @@
leaveSessionRest(currentSessionId)
.done(function() {
sessionChanged();
deferred.resolve(arguments);
deferred.resolve(arguments[0], arguments[1], arguments[2]);
})
.fail(function() {
deferred.reject(arguments);
deferred.reject(arguments[0], arguments[1], arguments[2]);
});
// 'unregister' for callbacks

View File

@ -133,6 +133,11 @@
rest.getSession(sessionId)
.done(function(response) {
session = response;
if(session && session.recording) {
context.JK.app.notify( { title: "Unable to Join Session", text: "The session is currently recording." }, null, true);
return;
}
if ("invitations" in session) {
var invitation;
// user has invitations for this session

View File

@ -220,6 +220,7 @@ context.JK.SyncViewer = class SyncViewer
updateTrackState: ($track) =>
clientInfo = $track.data('client-info')
serverInfo = $track.data('server-info')
myTrack = serverInfo.user.id == context.JK.currentUserId
# determine client state
clientStateMsg = 'UNKNOWN'
@ -261,7 +262,7 @@ context.JK.SyncViewer = class SyncViewer
uploadStateClass = 'error'
uploadState = @uploadStates.too_many_upload_failures
else
if serverInfo.user.id == context.JK.currentUserId
if myTrack
if clientInfo?
if clientInfo.local_state == 'HQ'
uploadStateMsg = 'PENDING UPLOAD'
@ -282,7 +283,7 @@ context.JK.SyncViewer = class SyncViewer
else
uploadStateMsg = 'UPLOADED'
uploadStateClass = 'uploaded'
if serverInfo.user.id == context.JK.currentUserId
if myTrack
uploadState = @uploadStates.me_uploaded
else
uploadState = @uploadStates.them_uploaded
@ -301,6 +302,30 @@ context.JK.SyncViewer = class SyncViewer
$uploadStateMsg.text(uploadStateMsg)
$uploadStateProgress.css('width', '0')
# this allows us to make styling decisions based on the combination of both client and upload state.
$track.addClass("clientState-#{clientStateClass}").addClass("uploadState-#{uploadStateClass}")
$clientRetry = $clientState.find('.retry')
$uploadRetry = $uploadState.find('.retry')
if gon.isNativeClient
# handle client state
# only show RETRY button if you have a SQ or if it's missing, and it's been uploaded already
if (clientState == @clientStates.sq or clientState == @clientStates.missing) and (uploadState == @uploadStates.me_uploaded or uploadState == @uploadStates.them_uploaded)
$clientRetry.show()
else
$clientRetry.hide()
# only show RETRY button if you have the HQ track, it's your track, and the server doesn't yet have it
if myTrack and @clientStates.hq and (uploadState == @uploadStates.error or uploadState == @uploadStates.me_upload_soon)
$uploadRetry.show()
else
$uploadRetry.hide()
else
$clientRetry.hide()
$uploadRetry.hide()
associateClientInfo: (recording) =>
for clientInfo in recording.local_tracks
$track = @list.find(".recorded-track[data-recording-id='#{recording.recording_id}'][data-client-track-id='#{clientInfo.client_track_id}']")
@ -485,7 +510,14 @@ context.JK.SyncViewer = class SyncViewer
recordingInfo = null
if userSync == 'fake'
recordingInfo = arguments[1]
userSync = { recording_id: recordingInfo.id, duration: recordingInfo.duration, fake:true }
# sift through the recorded_tracks in here; if they are marked discarded, then we can also mark this one discarded too
discarded = true
for claim in recordingInfo.claimed_recordings
if claim.user_id == context.JK.currentUserId
discarded = false
break
userSync = { recording_id: recordingInfo.id, duration: recordingInfo.duration, fake:true, discarded: discarded }
$mix = $(context._.template(@templateMix.html(), userSync, {variable: 'data'}))
else
$mix = $(context._.template(@templateMix.html(), userSync, {variable: 'data'}))
@ -504,14 +536,16 @@ context.JK.SyncViewer = class SyncViewer
$track.data('sync-viewer', this)
$clientState = $track.find('.client-state')
$uploadState = $track.find('.upload-state')
$clientState.find('.retry').click(this.retryDownloadRecordedTrack)
$uploadState.find('.retry').click(this.retryUploadRecordedTrack)
$clientStateRetry = $clientState.find('.retry')
$clientStateRetry.click(this.retryDownloadRecordedTrack)
$uploadStateRetry = $uploadState.find('.retry')
$uploadStateRetry.click(this.retryUploadRecordedTrack)
context.JK.bindHoverEvents($track)
context.JK.bindInstrumentHover($track, {positions:['top'], shrinkToFit: true});
context.JK.hoverBubble($clientState, this.onHoverOfStateIndicator, {width:'450px', closeWhenOthersOpen: true, positions:['left']})
context.JK.hoverBubble($uploadState, this.onHoverOfStateIndicator, {width:'450px', closeWhenOthersOpen: true, positions:['right']})
$clientState.addClass('is-native-client') if context.jamClient.IsNativeClient()
$uploadState.addClass('is-native-client') if context.jamClient.IsNativeClient()
$clientState.addClass('is-native-client') if gon.isNativeClient
$uploadState.addClass('is-native-client') if gon.isNativeClient
$track
createStreamMix: (userSync) =>
@ -523,8 +557,8 @@ context.JK.SyncViewer = class SyncViewer
$uploadState.find('.retry').click(this.retryUploadRecordedTrack)
context.JK.hoverBubble($clientState, this.onStreamMixHover, {width:'450px', closeWhenOthersOpen: true, positions:['left']})
context.JK.hoverBubble($uploadState, this.onStreamMixHover, {width:'450px', closeWhenOthersOpen: true, positions:['right']})
$clientState.addClass('is-native-client') if context.jamClient.IsNativeClient()
$uploadState.addClass('is-native-client') if context.jamClient.IsNativeClient()
$clientState.addClass('is-native-client') if gon.isNativeClient
$uploadState.addClass('is-native-client') if gon.isNativeClient
$track
exportRecording: (e) =>

View File

@ -180,6 +180,7 @@ $fair: #cc9900;
@mixin client-state-box {
.client-state {
position:relative;
text-align:center;
@ -193,7 +194,6 @@ $fair: #cc9900;
&.error {
background-color: $error;
&.is-native-client { .retry { display:inline-block; } }
}
&.hq {
@ -202,12 +202,10 @@ $fair: #cc9900;
&.sq {
background-color: $good;
&.is-native-client { .retry { display:inline-block; } }
}
&.missing {
background-color: $error;
&.is-native-client { .retry { display:inline-block; } }
}
&.discarded {
@ -240,7 +238,6 @@ $fair: #cc9900;
&.error {
background-color: $error;
&.is-native-client { .retry { display:inline-block; } }
}
&.missing {
@ -249,7 +246,6 @@ $fair: #cc9900;
&.upload-soon {
background-color: $fair;
&.is-native-client { .retry { display:inline-block; } }
}
&.uploaded {
@ -278,7 +274,7 @@ $fair: #cc9900;
color:white;
&.still-uploading { background-color: $fair; }
&.discard {background-color: $unknown; }
&.discarded {background-color: $unknown; }
&.unknown { background-color: $unknown; }
&.error { background-color: $error; }
&.mixed { background-color: $good; }

View File

@ -1,6 +1,7 @@
class ApiRecordingsController < ApiController
before_filter :api_signed_in_user, :except => [ :add_like ]
before_filter :lookup_recording, :only => [ :show, :stop, :claim, :discard, :keep ]
before_filter :lookup_recorded_track, :only => [ :download, :upload_next_part, :upload_sign, :upload_part_complete, :upload_complete ]
before_filter :lookup_recorded_video, :only => [ :video_upload_sign, :video_upload_start, :video_upload_complete ]
@ -239,6 +240,61 @@ class ApiRecordingsController < ApiController
end
def upload_next_part_stream_mix
length = params[:length]
md5 = params[:md5]
@quick_mix.upload_next_part(length, md5)
if @quick_mix.errors.any?
response.status = :unprocessable_entity
# this is not typical, but please don't change this line unless you are sure it won't break anything
# this is needed because after_rollback in the RecordedTrackObserver touches the model and something about it's
# state doesn't cause errors to shoot out like normal.
render :json => { :errors => @quick_mix.errors }, :status => 422
else
result = {
:part => @quick_mix.next_part_to_upload,
:offset => @quick_mix.file_offset.to_s
}
render :json => result, :status => 200
end
end
def upload_sign_stream_mix
render :json => @quick_mix.upload_sign(params[:md5]), :status => 200
end
def upload_part_complete_stream_mix
part = params[:part]
offset = params[:offset]
@quick_mix.upload_part_complete(part, offset)
if @quick_mix.errors.any?
response.status = :unprocessable_entity
respond_with @quick_mix
else
render :json => {}, :status => 200
end
end
def upload_complete_stream_mix
@quick_mix.upload_complete
if @quick_mix.errors.any?
response.status = :unprocessable_entity
respond_with @quick_mix
return
else
render :json => {}, :status => 200
end
end
def upload_next_part_stream_mix
length = params[:length]
md5 = params[:md5]
@ -305,14 +361,14 @@ class ApiRecordingsController < ApiController
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_track.recording.has_access?(current_user)
end
def lookup_recorded_video
@recorded_video = RecordedVideo.find_by_recording_id_and_client_video_source_id!(params[:id], params[:video_id])
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_video.recording.has_access?(current_user)
end
def lookup_stream_mix
@quick_mix = QuickMix.find_by_recording_id_and_user_id!(params[:id], current_user.id)
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @quick_mix.recording.has_access?(current_user)
end
def lookup_recorded_video
@recorded_video = RecordedVideo.find_by_recording_id_and_client_video_source_id!(params[:id], params[:video_id])
raise PermissionError, ValidationMessages::PERMISSION_VALIDATION_ERROR unless @recorded_video.recording.has_access?(current_user)
end
end # class

View File

@ -18,7 +18,7 @@
<% end %>
<div class="recordings-page">
<% if @claimed_recording.is_public %>
<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %>
<div class="landing-band">
<% unless @claimed_recording.recording.band.blank? %>
<div class="landing-avatar">
@ -97,7 +97,7 @@
<% end %>
</div>
<% if @claimed_recording.is_public %>
<% if @claimed_recording.is_public || @claimed_recording.recording.has_access?(current_user) %>
<% if signed_in? %>
<% unless @claimed_recording.recording.band.nil? %>
<%= render :partial => "shared/landing_sidebar", :locals => {:user => @claimed_recording.recording.band, :recent_history => @claimed_recording.recording.band.recent_history} %>

View File

@ -179,6 +179,8 @@ if defined?(Bundler)
config.audiomixer_path = "/var/lib/audiomixer/audiomixer/audiomixerapp"
config.ffmpeg_path = ENV['FFMPEG_PATH'] || (File.exist?('/usr/local/bin/ffmpeg') ? '/usr/local/bin/ffmpeg' : '/usr/bin/ffmpeg')
config.normalize_ogg_path = ENV['NORMALIZE_OGG_PATH'] || (File.exist?('/usr/local/bin/normalize-ogg') ? '/usr/local/bin/normalize-ogg' : '/usr/bin/normalize-ogg')
config.normalize_mp3_path = ENV['NORMALIZE_MP3_PATH'] || (File.exist?('/usr/local/bin/normalize-mp3') ? '/usr/local/bin/normalize-mp3' : '/usr/bin/normalize-mp3')
# if it looks like linux, use init.d script; otherwise use kill
config.icecast_reload_cmd = ENV['ICECAST_RELOAD_CMD'] || (File.exist?('/usr/local/bin/icecast2') ? "bash -l -c #{Shellwords.escape("sudo /etc/init.d/icecast2 reload")}" : "bash -l -c #{Shellwords.escape("kill -1 `ps -f | grep /usr/local/bin/icecast | grep -v grep | awk \'{print $2}\'`")}")

View File

@ -401,8 +401,8 @@ SampleApp::Application.routes.draw do
match '/recordings/:id/comments' => 'api_recordings#add_comment', :via => :post, :as => 'api_recordings_add_comment'
match '/recordings/:id/likes' => 'api_recordings#add_like', :via => :post, :as => 'api_recordings_add_like'
match '/recordings/:id/discard' => 'api_recordings#discard', :via => :post, :as => 'api_recordings_discard'
# Recordings - recorded_tracks
match '/recordings/:id/tracks/:track_id' => 'api_recordings#show_recorded_track', :via => :get, :as => 'api_recordings_show_recorded_track'
match '/recordings/:id/tracks/:track_id/download' => 'api_recordings#download', :via => :get, :as => 'api_recordings_download'
match '/recordings/:id/tracks/:track_id/upload_next_part' => 'api_recordings#upload_next_part', :via => :get

View File

@ -11,6 +11,26 @@ describe "Music Session", :js => true, :type => :feature, :capybara_feature => t
subject { page }
describe "recorded session" do
before(:each) do
ActiveMusicSession.delete_all
MusicSession.delete_all
end
let(:searcher) { FactoryGirl.create(:user) }
let(:creator) { FactoryGirl.create(:user) }
let(:conn) { FactoryGirl.create(:connection, :user => creator) }
let(:description) {'hot recordings in here'}
let(:session) {FactoryGirl.create(:active_music_session, creator:creator, description: description)}
let(:recording) {FactoryGirl.create(:recording, music_session: session, owner: creator)}
it "won't let user join" do
recording.touch
join_session(searcher, description:'hot recordings in here', no_verify:true)
find('#notification p').text('The session is currently recording.')
end
end
context "last person" do
before(:each) do

View File

@ -1,6 +1,6 @@
require 'spec_helper'
describe "Landing", :js => true, :type => :feature, :capybara_feature => true do
describe "Landing" do
let (:user) { FactoryGirl.create(:user) }
@ -10,41 +10,70 @@ describe "Landing", :js => true, :type => :feature, :capybara_feature => true do
Recording.delete_all
end
before(:each) do
MusicSession.delete_all
sign_in_poltergeist(user)
end
let (:claimed_recording) { FactoryGirl.create(:claimed_recording) }
let (:recording) { claimed_recording.recording }
it "should render comments" do
describe "no js required" do
recording = ClaimedRecording.first
comment = "test comment"
timestamp = "less than a minute ago"
url = "/recordings/#{claimed_recording.id}"
visit url
it "shows private recording to someone who was in the session" do
# make the session hidden
claimed_recording.is_public = false
claimed_recording.save!
fill_in "txtRecordingComment", with: comment
find('#btnPostComment').trigger(:click)
visit "/recordings/#{claimed_recording.id}"
# (1) Test a user creating a comment and ensure it displays.
# and verify that after we visit the page, we can see the name of it
find('strong', text: 'RECORDING NOT FOUND')
# comment body
find('div.comment-text', text: comment)
# log in the user who was a part of the session
sign_in(claimed_recording.user)
# timestamp
find('div.comment-timestamp', text: timestamp)
visit "/recordings/#{claimed_recording.id}"
# (2) Test a user visiting a landing page with an existing comment.
# and verify that after we visit the page, we can see the name of it
find('h2', text: claimed_recording.name)
end
end
# re-visit page to reload from database
visit url
# comment body
find('div.comment-text', text: comment)
describe "js required", :js => true, :type => :feature, :capybara_feature => true do
# timestamp
find('div.comment-timestamp', text: timestamp)
before(:each) do
MusicSession.delete_all
sign_in_poltergeist(user)
end
it "should render comments" do
recording = ClaimedRecording.first
comment = "test comment"
timestamp = "less than a minute ago"
url = "/recordings/#{claimed_recording.id}"
visit url
fill_in "txtRecordingComment", with: comment
find('#btnPostComment').trigger(:click)
# (1) Test a user creating a comment and ensure it displays.
# comment body
find('div.comment-text', text: comment)
# timestamp
find('div.comment-timestamp', text: timestamp)
# (2) Test a user visiting a landing page with an existing comment.
# re-visit page to reload from database
visit url
# comment body
find('div.comment-text', text: comment)
# timestamp
find('div.comment-timestamp', text: timestamp)
end
end
end

View File

@ -457,9 +457,11 @@ def join_session(joiner, options)
# verify the session description is seen by second client
expect(page).to have_text(description)
find('.join-link').trigger(:click)
find('#btn-accept-terms').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
unless options[:no_verify]
find('#btn-accept-terms').trigger(:click)
expect(page).to have_selector('h2', text: 'my tracks')
find('#session-screen .session-mytracks .session-track')
end
end
end

View File

@ -80,7 +80,7 @@ module JamWebsockets
@router.periodical_check_connections
EventMachine::PeriodicTimer.new(2) do
sane_logging { @router.periodical_check_connections }
safety_net { sane_logging { @router.periodical_check_connections } }
end
end
@ -89,7 +89,7 @@ module JamWebsockets
@router.periodical_check_clients
EventMachine::PeriodicTimer.new(30) do
sane_logging { @router.periodical_check_clients }
safety_net { sane_logging { @router.periodical_check_clients } }
end
end
@ -98,16 +98,29 @@ module JamWebsockets
@router.periodical_flag_connections
EventMachine::PeriodicTimer.new(2) do
sane_logging { @router.periodical_flag_connections }
safety_net { sane_logging { @router.periodical_flag_connections } }
end
end
def start_stats_dump
EventMachine::PeriodicTimer.new(60) do
@router.periodical_stats_dump
safety_net { @router.periodical_stats_dump }
end
end
# this was added for this reason: https://jamkazam.atlassian.net/browse/VRFS-2425
# if an unhandled exception occurs in PeriodicTimer, it just kills all future timers; doesn't kill the app.
# not really what you want.
# so, we signal to Bugsnag, so we know really bad stuff is happening, but we also move
def safety_net(&blk)
begin
blk.call
rescue => e
Bugsnag.notify(e)
@log.error("unhandled exception in EM Timer #{e}")
end
end
def sane_logging(&blk)
# used around repeated transactions that cause too much ActiveRecord::Base logging
# example is handling heartbeats