2012-10-21 15:05:06 +00:00
module JamRuby
2012-11-11 01:07:17 +00:00
# All writes should occur through the ConnectionManager for the connection table
2012-10-21 15:05:06 +00:00
# Reads can occur freely elsewhere, though
# Because connections are tied to the websocket-connection and we bookkeep them in the database purely
# for 'SQL convenience', this is a obvious place we can go away from a database
# as an optimization if we find it's too much db traffic created'
# At a minimum, though, we could make connections an UNLOGGED table because if the database crashes,
2014-02-22 04:21:31 +00:00
# all clients should reconnect and re-establish their connection anyway
2012-10-21 15:05:06 +00:00
#
# All methods in here could also be refactored as stored procedures, if we stick with a database.
# This may make sense in the short term if we are still managing connections in the database, but
# we move to the node-js in the websocket gateway (because the websocket gateway needs to call some of these methods).
# Or of course we could just port the relevant methods to node-js
2013-08-07 15:35:27 +00:00
#
# Also we don't send notifications from ConnectionManager;
# we just return enough data so that a caller can make the determination if it needs to
2012-11-11 01:07:17 +00:00
class ConnectionManager < BaseManager
2012-10-21 15:05:06 +00:00
2012-10-23 00:59:35 +00:00
def initialize ( options = { } )
2012-11-11 01:07:17 +00:00
super ( options )
2012-10-21 15:05:06 +00:00
@log = Logging . logger [ self ]
end
2012-10-21 01:55:49 +00:00
2012-10-21 15:05:06 +00:00
def update_staleness ( )
#TODO
2012-10-21 01:55:49 +00:00
end
2013-03-22 03:18:41 +00:00
##### TODO: refactored to notification.rb but left here for backwards compatibility w/ connection_manager_spec.rb
def gather_friends ( connection , user_id )
2015-12-08 02:25:43 +00:00
friend_ids = [ ]
connection . exec ( " SELECT f1.friend_id as friend_id FROM friendships f1 WHERE f1.user_id = $1 AND f1.friend_id IN (SELECT f2.user_id FROM friendships f2 WHERE f2.friend_id = $1) " , [ user_id ] ) do | friend_results |
friend_results . each do | friend_result |
friend_ids . push ( friend_result [ 'friend_id' ] )
2013-03-22 03:18:41 +00:00
end
2015-12-08 02:25:43 +00:00
end
return friend_ids
2013-03-22 03:18:41 +00:00
end
2014-01-21 14:51:03 +00:00
# this simulates music_session destroy callbacks with activerecord
def before_destroy_music_session ( music_session_id )
2014-05-06 21:17:26 +00:00
music_session = ActiveMusicSession . find_by_id ( music_session_id )
2014-01-21 14:51:03 +00:00
music_session . before_destroy if music_session
end
2015-12-08 02:25:43 +00:00
2014-02-22 04:21:31 +00:00
# reclaim the existing connection, if ip_address is not nil then perhaps a new address as well
2014-09-24 19:27:56 +00:00
def reconnect ( conn , channel_id , reconnect_music_session_id , ip_address , connection_stale_time , connection_expire_time , udp_reachable , gateway )
2013-08-07 15:35:27 +00:00
music_session_id = nil
reconnected = false
# we will reconnect the same music_session that the connection was previously in,
# if it matches the same value currently in the database for music_session_id
music_session_id_expression = 'NULL'
2014-03-04 02:30:34 +00:00
joined_session_at_expression = 'NULL'
2013-08-07 15:35:27 +00:00
unless reconnect_music_session_id . nil?
music_session_id_expression = " (CASE WHEN music_session_id=' #{ reconnect_music_session_id } ' THEN music_session_id ELSE NULL END) "
2014-03-03 22:13:23 +00:00
joined_session_at_expression = " (CASE WHEN music_session_id=' #{ reconnect_music_session_id } ' THEN NOW() ELSE NULL END) "
2013-08-07 15:35:27 +00:00
end
2014-03-03 05:16:48 +00:00
if ip_address and ! ip_address . eql? ( conn . ip_address )
2014-02-24 21:19:46 +00:00
# turn ip_address string into a number, then fetch the isp and block records and update location info
2014-02-23 05:39:20 +00:00
addr = JamIsp . ip_to_num ( ip_address )
2014-02-23 23:24:25 +00:00
#puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
2014-02-23 05:39:20 +00:00
isp = JamIsp . lookup ( addr )
2014-02-23 23:24:25 +00:00
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
2015-12-08 02:25:43 +00:00
if isp . nil? then
ispid = 0
else
ispid = isp . coid
end
2014-02-23 05:39:20 +00:00
block = GeoIpBlocks . lookup ( addr )
2014-02-23 23:24:25 +00:00
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
2015-12-08 02:25:43 +00:00
if block . nil? then
locid = 0
else
locid = block . locid
end
2014-02-23 23:24:25 +00:00
2014-07-20 02:11:16 +00:00
location = GeoIpLocations . find_by_locid ( locid )
2014-07-22 19:36:45 +00:00
if location . nil? || isp . nil? || block . nil?
locidispid = nil
2014-02-24 21:19:46 +00:00
else
locidispid = locid * 1000000 + ispid
end
2014-02-23 05:39:20 +00:00
2014-02-25 03:25:34 +00:00
conn . ip_address = ip_address
2014-03-03 05:16:48 +00:00
conn . addr = addr
2014-02-25 03:25:34 +00:00
conn . locidispid = locidispid
conn . save! ( validate : false )
2014-02-22 04:21:31 +00:00
end
2013-08-07 15:35:27 +00:00
2014-09-17 15:09:53 +00:00
# if udp_reachable is nil, it means it's unknown. Since this is a reconnect, we'll, preserve existing value in this case
# otherwise, pass in the value of boolean udp_reachable var
udp_reachable_value = udp_reachable . nil? ? 'udp_reachable' : udp_reachable
2013-08-07 15:35:27 +00:00
sql = <<SQL
2015-03-13 02:53:23 +00:00
UPDATE connections SET ( channel_id , aasm_state , updated_at , music_session_id , joined_session_at , stale_time , expire_time , udp_reachable , gateway , is_network_testing , metronome_open ) = ( '#{channel_id}' , '#{Connection::CONNECT_STATE.to_s}' , NOW ( ) , #{music_session_id_expression}, #{joined_session_at_expression}, #{connection_stale_time}, #{connection_expire_time}, #{udp_reachable_value}, '#{gateway}', FALSE, FALSE)
2014-09-24 19:27:56 +00:00
WHERE
2013-02-22 01:34:04 +00:00
client_id = '#{conn.client_id}'
2013-08-07 15:35:27 +00:00
RETURNING music_session_id
2013-02-22 01:34:04 +00:00
SQL
2013-08-07 15:35:27 +00:00
self . pg_conn . exec ( sql ) do | result |
if result . cmd_tuples == 1
music_session_id = result [ 0 ] [ 'music_session_id' ]
end
end
# we tell the client they reconnected if they specified a reconnect music_session_id, and if that is now the
# current value in the database
reconnected = true if ! reconnect_music_session_id . nil? && reconnect_music_session_id == music_session_id
return music_session_id , reconnected
2013-02-22 01:34:04 +00:00
end
2013-08-07 15:35:27 +00:00
# returns the music_session_id, if any, associated with the client
2013-02-21 17:36:58 +00:00
def flag_connection_stale_with_client_id ( client_id )
2013-08-07 15:35:27 +00:00
music_session_id = nil
2013-02-21 17:36:58 +00:00
sql = <<SQL
UPDATE connections SET aasm_state = '#{Connection::STALE_STATE.to_s}'
WHERE
client_id = '#{client_id}' AND
aasm_state = '#{Connection::CONNECT_STATE.to_s}'
2013-08-07 15:35:27 +00:00
RETURNING music_session_id
2013-02-21 17:36:58 +00:00
SQL
2013-08-07 15:35:27 +00:00
self . pg_conn . exec ( sql ) do | result |
# if we did update a client to stale, retriee music_session_id
if result . cmd_tuples == 1
music_session_id = result [ 0 ] [ 'music_session_id' ]
end
end
music_session_id
2013-02-21 17:36:58 +00:00
end
2013-02-11 06:26:46 +00:00
# flag connections as stale
2014-09-24 19:27:56 +00:00
def flag_stale_connections ( gateway_name )
2013-02-06 13:11:05 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
conn = connection_manager . pg_conn
2014-09-24 19:27:56 +00:00
sql = " UPDATE connections SET aasm_state = ' #{ Connection :: STALE_STATE . to_s } ' WHERE updated_at < (NOW() - (interval '1 second' * stale_time)) AND aasm_state = ' #{ Connection :: CONNECT_STATE . to_s } ' AND gateway = ' #{ gateway_name } ' "
conn . exec ( sql )
2013-02-06 13:11:05 +00:00
end
end
2013-08-31 19:54:43 +00:00
# NOTE this is only used for testing purposes;
# actual deletes will be processed in the websocket context which cleans up dependencies
2014-09-24 19:27:56 +00:00
def expire_stale_connections ( gateway_name )
self . stale_connection_client_ids ( gateway_name ) . each { | client | self . delete_connection ( client [ :client_id ] ) }
2013-02-21 17:36:58 +00:00
end
# expiring connections in stale state, which deletes them
2014-09-24 19:27:56 +00:00
def stale_connection_client_ids ( gateway_name )
2014-04-30 03:01:28 +00:00
clients = [ ]
2012-11-30 15:23:43 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
conn = connection_manager . pg_conn
2014-09-24 19:27:56 +00:00
sql = " SELECT client_id, music_session_id, user_id, client_type FROM connections WHERE updated_at < (NOW() - (interval '1 second' * expire_time)) AND gateway = ' #{ gateway_name } ' "
2013-02-11 06:26:46 +00:00
conn . exec ( sql ) do | result |
2013-08-07 15:35:27 +00:00
result . each { | row |
client_id = row [ 'client_id' ]
music_session_id = row [ 'music_session_id' ]
user_id = row [ 'user_id' ]
2014-04-30 03:01:28 +00:00
client_type = row [ 'client_type' ]
clients << { client_id : client_id , music_session_id : music_session_id , client_type : client_type , user_id : user_id }
2013-08-07 15:35:27 +00:00
}
2012-11-30 15:23:43 +00:00
end
2012-10-21 15:05:06 +00:00
end
2014-04-30 03:01:28 +00:00
clients
2012-10-21 01:55:49 +00:00
end
2012-10-23 00:59:35 +00:00
2013-08-07 15:35:27 +00:00
# returns the number of connections that this user currently has across all clients
# this number is used by notification logic elsewhere to know
# 'oh the user joined for the 1st time, so send a friend update', or
# 'don't bother because the user has connected somewhere else already'
2016-03-04 15:06:13 +00:00
def create_connection ( user_id , client_id , channel_id , ip_address , client_type , connection_stale_time , connection_expire_time , udp_reachable , gateway , is_jamblaster , & blk )
2014-03-07 20:20:34 +00:00
# validate client_type
raise " invalid client_type: #{ client_type } " if client_type != 'client' && client_type != 'browser'
2013-08-07 15:35:27 +00:00
count = 0
2012-11-30 15:23:43 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
conn = connection_manager . pg_conn
2014-02-22 04:21:31 +00:00
2014-02-24 21:19:46 +00:00
# turn ip_address string into a number, then fetch the isp and block records
2014-02-23 05:39:20 +00:00
2014-02-23 04:00:32 +00:00
addr = JamIsp . ip_to_num ( ip_address )
2014-02-23 23:24:25 +00:00
#puts("============= JamIsp.ip_to_num returns #{addr} for #{ip_address} =============")
2014-02-23 04:00:32 +00:00
isp = JamIsp . lookup ( addr )
2014-02-23 23:24:25 +00:00
#puts("============= JamIsp.lookup returns #{isp.inspect} for #{addr} =============")
2015-12-08 02:25:43 +00:00
if isp . nil? then
ispid = 0
else
ispid = isp . coid
end
2014-02-23 04:00:32 +00:00
2014-02-23 05:39:20 +00:00
block = GeoIpBlocks . lookup ( addr )
2014-02-23 23:24:25 +00:00
#puts("============= GeoIpBlocks.lookup returns #{block.inspect} for #{addr} =============")
2015-12-08 02:25:43 +00:00
if block . nil? then
locid = 0
else
locid = block . locid
end
2014-02-23 23:24:25 +00:00
2014-07-20 02:11:16 +00:00
location = GeoIpLocations . find_by_locid ( locid )
2014-07-22 19:36:45 +00:00
if location . nil? || isp . nil? || block . nil?
locidispid = nil
2014-02-24 21:19:46 +00:00
else
locidispid = locid * 1000000 + ispid
end
2014-02-22 04:21:31 +00:00
2012-11-30 15:23:43 +00:00
lock_connections ( conn )
2016-03-04 15:06:13 +00:00
conn . exec ( " INSERT INTO connections (user_id, client_id, channel_id, ip_address, client_type, addr, locidispid, aasm_state, stale_time, expire_time, udp_reachable, gateway, is_jamblaster) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) " ,
[ user_id , client_id , channel_id , ip_address , client_type , addr , locidispid , Connection :: CONNECT_STATE . to_s , connection_stale_time , connection_expire_time , udp_reachable , gateway , is_jamblaster ] ) . clear
2012-11-30 15:23:43 +00:00
2016-03-04 15:06:13 +00:00
if user_id
# we just created a new connection-if this is the first time the user has shown up, we need to send out a message to his friends
conn . exec ( " SELECT count(user_id) FROM connections WHERE user_id = $1 " , [ user_id ] ) do | result |
count = result . getvalue ( 0 , 0 ) . to_i
# we're passing all this stuff so that the user record might be updated as well...
blk . call ( conn , count ) unless blk . nil?
end
2012-10-21 01:55:49 +00:00
end
2016-03-04 15:06:13 +00:00
2013-08-07 15:35:27 +00:00
return count
2012-10-21 01:55:49 +00:00
end
end
2012-10-21 15:05:06 +00:00
# once a connection is known gone (whether timeout or because a TCP connection is observed lost)
# this code is responsible for all cleanup logic associated with a connection going away
2013-08-07 15:35:27 +00:00
# returns how many connections are left for this user; this data is used by callers to know whether
# to tell friends if the user is offline (count==0) or not (count > 0)
# If a blk is passed in, on success, count is also passed back an the db connection, allowing for
# notifications to go out within the table log. music_session_id is also passed, if the music_session still exists
# and this connection was in a session
def delete_connection ( client_id , & blk )
2012-10-21 01:55:49 +00:00
2013-08-07 15:35:27 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
2012-11-30 15:23:43 +00:00
conn = connection_manager . pg_conn
2013-08-07 15:35:27 +00:00
count = 0
2012-11-30 15:23:43 +00:00
user_id = nil
music_session_id = nil
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
lock_connections ( conn )
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
previous_music_session_id = check_already_session ( conn , client_id )
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
conn . exec ( " DELETE FROM connections WHERE client_id = $1 RETURNING user_id, music_session_id " , [ client_id ] ) do | result |
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
if result . cmd_tuples == 0
# the client is already gone from the database... do nothing but log error
@log . warn ( " unable to delete client #{ client_id } " )
return
elsif result . cmd_tuples == 1
user_id = result [ 0 ] [ 'user_id' ]
2013-08-07 15:35:27 +00:00
music_session_id = result [ 0 ] [ 'music_session_id' ]
2012-10-21 15:05:06 +00:00
2012-11-30 15:23:43 +00:00
else
raise Exception , 'uniqueness constraint has been lost on client_id'
end
2012-10-21 15:05:06 +00:00
end
2012-11-30 15:23:43 +00:00
session_checks ( conn , previous_music_session_id , user_id )
2012-10-21 15:05:06 +00:00
2012-11-30 15:23:43 +00:00
# since we did delete a row, check and see if any more connections for that user exist
# if we are down to zero, send out user gone message
conn . exec ( " SELECT count(user_id) FROM connections where user_id = $1 " , [ user_id ] ) do | result |
2013-08-07 15:35:27 +00:00
count = result . getvalue ( 0 , 0 ) . to_i
2012-10-21 15:05:06 +00:00
end
2012-11-30 15:23:43 +00:00
# same for session-if we are down to the last participant, delete the session
unless music_session_id . nil?
2014-01-21 14:51:03 +00:00
before_destroy_music_session ( music_session_id )
2014-05-06 22:50:41 +00:00
result = conn . exec ( " DELETE FROM active_music_sessions WHERE id = $1 AND 0 = (select count(music_session_id) FROM connections where music_session_id = $1) " , [ music_session_id ] )
2013-08-07 15:35:27 +00:00
if result . cmd_tuples == 1
music_session_id = nil
end
2012-11-30 15:23:43 +00:00
end
2013-08-07 15:35:27 +00:00
2013-08-31 19:54:43 +00:00
blk . call ( conn , count , music_session_id , user_id ) unless blk . nil?
2013-08-07 15:35:27 +00:00
return count
2012-10-21 01:55:49 +00:00
end
2012-10-21 15:05:06 +00:00
end
def check_already_session ( conn , client_id )
conn . exec ( " SELECT music_session_id FROM connections WHERE client_id = $1 " , [ client_id ] ) do | result |
if result . num_tuples == 1
previous_music_session_id = result . getvalue ( 0 , 0 )
return previous_music_session_id
elsif result . num_tuples == 0
# there is no connection found matching this criteria; we are done.
@log . debug ( " when checking for existing session, no connection found with client= #{ client_id } " )
return nil
else
@log . error ( " connection table data integrity violation; multiple rows found. client_id= #{ client_id } " )
raise Exception , " connection table data integrity violation; multiple rows found. client_id= #{ client_id } "
end
2012-10-21 01:55:49 +00:00
end
end
2012-10-21 15:05:06 +00:00
def session_checks ( conn , previous_music_session_id , user_id )
unless previous_music_session_id . nil?
# TODO: send notification to friends that this user left this session?
@log . debug ( " user #{ user_id } left music_session #{ previous_music_session_id } " )
2013-07-23 20:22:03 +00:00
# destroy the music_session if it's empty
num_participants = nil
2015-12-08 02:25:43 +00:00
conn . exec ( " SELECT count(*) FROM connections WHERE music_session_id = $1 " ,
2013-07-23 20:22:03 +00:00
[ previous_music_session_id ] ) do | result |
num_participants = result . getvalue ( 0 , 0 ) . to_i
end
if num_participants == 0
# delete the music_session
2014-01-21 14:51:03 +00:00
before_destroy_music_session ( previous_music_session_id )
2014-05-06 22:50:41 +00:00
conn . exec ( " DELETE from active_music_sessions WHERE id = $1 " ,
2013-07-23 20:22:03 +00:00
[ previous_music_session_id ] ) do | result |
if result . cmd_tuples == 1
# music session deleted!
@log . debug ( " deleted music session #{ previous_music_session_id } " )
2014-05-06 13:34:38 +00:00
JamRuby :: MusicSession . removed_music_session ( previous_music_session_id )
2013-07-23 20:22:03 +00:00
elsif 1 < result . cmd_tuples
msg = " music_sessions table data integrity violation; multiple rows found with music_session_id= #{ previous_music_session_id } "
@log . error ( msg )
raise Exception , msg
end
end
2014-01-05 03:47:23 +00:00
else
2015-02-18 22:22:24 +00:00
conn . exec ( " UPDATE active_music_sessions set backing_track_initiator_id = NULL, backing_track_path = NULL where backing_track_initiator_id = $1 and id = $2 " ,
[ user_id , previous_music_session_id ] )
conn . exec ( " UPDATE active_music_sessions set metronome_initiator_id = NULL, metronome_active = FALSE where metronome_initiator_id = $1 and id = $2 " ,
[ user_id , previous_music_session_id ] )
2014-01-05 03:47:23 +00:00
#ensure that there is no active claimed recording if the owner of that recording left the session
2014-05-06 22:50:41 +00:00
conn . exec ( " UPDATE active_music_sessions set claimed_recording_id = NULL, claimed_recording_initiator_id = NULL where claimed_recording_initiator_id = $1 and id = $2 " ,
2015-02-18 22:22:24 +00:00
[ user_id , previous_music_session_id ] )
2015-01-09 02:35:39 +00:00
conn . exec ( " UPDATE active_music_sessions set jam_track_id = NULL, jam_track_initiator_id = NULL where jam_track_initiator_id = $1 and id = $2 " ,
2015-02-18 22:22:24 +00:00
[ user_id , previous_music_session_id ] )
2015-12-08 02:25:43 +00:00
update_session_controller ( previous_music_session_id )
end
end
end
def update_session_controller ( music_session_id )
active_music_session = ActiveMusicSession . find ( music_session_id )
if active_music_session
music_session = active_music_session . music_session
2017-07-10 02:21:29 +00:00
if music_session . session_controller_id && ! active_music_session . users . exists? ( music_session . session_controller . id )
2015-12-08 02:25:43 +00:00
# find next in line, because the current 'session controller' is not part of the session
next_in_line ( music_session , active_music_session )
end
end
end
# determine who should be session controller after someone leaves
def next_in_line ( music_session , active_music_session )
session_users = active_music_session . users
# check friends 1st
session_friends = music_session . creator . friends && session_users
if session_friends . length > 0
music_session . session_controller = session_friends [ 0 ]
if music_session . save
active_music_session . tick_track_changes
Notification . send_tracks_changed ( active_music_session )
return
end
end
# check invited 2nd
invited = music_session . invited_musicians && session_users
if invited . length > 0
music_session . session_controller = invited [ 0 ]
if music_session . save
active_music_session . tick_track_changes
Notification . send_tracks_changed ( active_music_session )
return
2013-07-23 20:22:03 +00:00
end
2015-12-08 02:25:43 +00:00
end
# go by who joined earliest
2015-12-08 19:37:18 +00:00
earliest = active_music_session . connections . order ( :joined_session_at ) . first
2014-01-05 03:47:23 +00:00
2015-12-08 02:25:43 +00:00
if earliest
music_session . session_controller = earliest
if music_session . save
active_music_session . tick_track_changes
Notification . send_tracks_changed ( active_music_session )
return
end
2012-10-21 15:05:06 +00:00
end
2015-12-08 02:25:43 +00:00
music_session . creator
2012-10-21 15:05:06 +00:00
end
2016-10-27 23:24:54 +00:00
def join_music_session ( user , client_id , music_session , as_musician , tracks , audio_latency , client_role = nil , parent_client_id = nil , video_sources = nil )
2012-11-30 15:23:43 +00:00
connection = nil
2013-06-26 03:10:21 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
db_conn = connection_manager . pg_conn
2012-11-30 15:23:43 +00:00
2016-03-29 10:50:14 +00:00
connection = Connection . find_by_client_id ( client_id )
if connection . nil?
raise JamRecordNotFound . new ( " Unable to find connection by client_id #{ client_id } " , 'Connection' )
elsif connection . user_id . nil?
raise JamPermissionError , " no user_id associated with connection #{ client_id } "
elsif connection . user_id != user . id
raise JamPermissionError , " wrong user_id associated with connection #{ client_id } "
end
2014-05-01 03:01:43 +00:00
2016-10-27 23:24:54 +00:00
connection . join_the_session ( music_session , as_musician , tracks , user , audio_latency , client_role , parent_client_id , video_sources )
2016-05-09 21:47:55 +00:00
JamRuby :: MusicSessionUserHistory . join_music_session ( user . id , music_session . id , client_id )
2014-05-01 03:01:43 +00:00
# connection.music_session_id = music_session.id
# connection.as_musician = as_musician
# connection.joining_session = true
# connection.joined_session_at = Time.now
# associate_tracks(connection, tracks)
# connection.save
2012-11-30 15:23:43 +00:00
if connection . errors . any?
raise ActiveRecord :: Rollback
2015-12-08 02:25:43 +00:00
else
update_session_controller ( music_session . id )
2012-11-30 15:23:43 +00:00
end
2015-12-08 02:25:43 +00:00
2012-11-30 15:23:43 +00:00
end
2014-02-18 22:35:23 +00:00
connection
2012-11-30 15:23:43 +00:00
end
2016-03-25 18:49:53 +00:00
2013-08-07 15:35:27 +00:00
# if a blk is passed in, upon success, it will be called and you can issue notifications
# within the connection table lock
def leave_music_session ( user , connection , music_session , & blk )
2012-11-30 15:23:43 +00:00
ConnectionManager . active_record_transaction do | connection_manager |
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
conn = connection_manager . pg_conn
2012-10-21 01:55:49 +00:00
2012-11-30 15:23:43 +00:00
lock_connections ( conn )
2012-10-21 15:05:06 +00:00
2013-06-26 03:10:21 +00:00
music_session_id = music_session . id
user_id = user . id
2013-06-26 16:24:38 +00:00
client_id = connection . client_id
2013-06-26 03:10:21 +00:00
2012-11-30 15:23:43 +00:00
previous_music_session_id = check_already_session ( conn , client_id )
2012-10-21 15:05:06 +00:00
2012-11-30 15:23:43 +00:00
if previous_music_session_id == nil
@log . debug " the client is not in a session. user= #{ user_id } , client= #{ client_id } , music_session= #{ music_session_id } "
raise StateError , " not in session "
elsif previous_music_session_id != music_session_id
@log . debug " the client is in a different session. user= #{ user_id } , client= #{ client_id } , music_session= #{ music_session_id } "
raise StateError , " in a session different than that specified "
end
2012-10-21 15:05:06 +00:00
2012-11-30 15:23:43 +00:00
# can throw exception if the session is deleted just before this
2014-05-01 03:01:43 +00:00
conn . exec ( " UPDATE connections SET music_session_id = NULL, joined_session_at = NULL, as_musician = NULL WHERE client_id = $1 AND user_id = $2 AND music_session_id = $3 " , [ client_id , user_id , music_session_id ] ) do | result |
2012-11-30 15:23:43 +00:00
if result . cmd_tuples == 1
2013-06-26 03:10:21 +00:00
@log . debug ( " disassociated music_session with connection for client_id= #{ client_id } , user_id= #{ user_id } " )
2012-11-30 15:23:43 +00:00
2015-12-08 02:25:43 +00:00
update_session_controller ( music_session . id )
2013-07-30 17:26:26 +00:00
JamRuby :: MusicSessionUserHistory . removed_music_session ( user_id , music_session_id )
2012-11-30 15:23:43 +00:00
session_checks ( conn , previous_music_session_id , user_id )
2013-08-07 15:35:27 +00:00
blk . call ( ) unless blk . nil?
2012-11-30 15:23:43 +00:00
elsif result . cmd_tuples == 0
@log . debug " leave_music_session no connection found with client_id= #{ client_id } "
raise ActiveRecord :: RecordNotFound
else
@log . error ( " database failure or logic error; this path should be impossible if the table is locked (leave_music_session) " )
raise Exception , " locked table changed state "
end
2012-10-21 15:05:06 +00:00
end
end
end
2012-10-23 00:59:35 +00:00
2012-10-23 11:37:25 +00:00
def lock_connections ( conn )
2014-12-18 22:26:56 +00:00
#if APP_CONFIG.lock_connections
# conn.exec("LOCK connections IN EXCLUSIVE MODE").clear
#end
2012-10-23 11:37:25 +00:00
end
2012-10-21 15:05:06 +00:00
2014-05-01 03:01:43 +00:00
# def associate_tracks(connection, tracks)
# @log.debug "Tracks:"
# @log.debug tracks
# connection.tracks.clear()
#
# unless tracks.nil?
# tracks.each do |track|
# instrument = Instrument.find(track["instrument_id"])
# t = Track.new
# t.instrument = instrument
# t.connection = connection
# t.sound = track["sound"]
# t.client_track_id = track["client_track_id"]
# t.save
# connection.tracks << t
# end
# end
# end
2012-11-30 15:23:43 +00:00
end
2013-02-06 13:11:05 +00:00
end