diff --git a/Gemfile b/Gemfile index 32957b1df..d5a500310 100644 --- a/Gemfile +++ b/Gemfile @@ -20,6 +20,7 @@ gem 'actionmailer' gem 'sendgrid' gem 'aws-sdk', '1.8.0' gem 'carrierwave' +gem 'aasm', '3.0.16' if devenv gem 'jam_db', :path=> "#{workspace}/jam-db/target/ruby_package" diff --git a/lib/jam_ruby/connection_manager.rb b/lib/jam_ruby/connection_manager.rb index 360726691..119b1ca46 100644 --- a/lib/jam_ruby/connection_manager.rb +++ b/lib/jam_ruby/connection_manager.rb @@ -26,22 +26,56 @@ module JamRuby #TODO end - # remove stale connections - def remove_stale_connections(max_seconds) + # flag connections as stale + def flag_stale_connections(max_seconds) + ConnectionManager.active_record_transaction do |connection_manager| + conn = connection_manager.pg_conn + + sql =< public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval) + def login_ack(public_ip, client_id, token, heartbeat_interval, music_session_id) + login_ack = Jampb::LoginAck.new(:public_ip => public_ip, :client_id => client_id, :token => token, :heartbeat_interval => heartbeat_interval, :music_session_id => music_session_id) return Jampb::ClientMessage.new(:type => ClientMessage::Type::LOGIN_ACK, :route_to => CLIENT_TARGET, :login_ack => login_ack) end diff --git a/lib/jam_ruby/models/connection.rb b/lib/jam_ruby/models/connection.rb index 8f3a1e3f0..d66cfbd5a 100644 --- a/lib/jam_ruby/models/connection.rb +++ b/lib/jam_ruby/models/connection.rb @@ -1,3 +1,5 @@ +require 'aasm' + module JamRuby class Connection < ActiveRecord::Base @@ -20,6 +22,48 @@ module JamRuby validate :can_join_music_session, :if => :joining_session? after_save :require_at_least_one_track_when_in_session, :if => :joining_session? + include AASM + IDLE_STATE = :idle + CONNECT_STATE = :connected + STALE_STATE = :stale + EXPIRED_STATE = :expired + + aasm do + state IDLE_STATE, :initial => true + state CONNECT_STATE + state STALE_STATE + state EXPIRED_STATE + + event :connect do + transitions :from => IDLE_STATE, :to => CONNECT_STATE + transitions :from => STALE_STATE, :to => CONNECT_STATE + end + event :stale do + transitions :from => CONNECT_STATE, :to => STALE_STATE + transitions :from => IDLE_STATE, :to => STALE_STATE + end + event :expire, :after => :did_expire do + transitions :from => CONNECT_STATE, :to => EXPIRED_STATE + transitions :from => STALE_STATE, :to => EXPIRED_STATE + transitions :from => IDLE_STATE, :to => EXPIRED_STATE + end + end + + def state_message + case self.aasm_state.to_sym + when CONNECT_STATE + 'Connected' + when STALE_STATE + 'Stale' + else + 'Idle' + end + end + + def did_expire + self.destroy + end + def joining_session? return joining_session end @@ -76,4 +120,4 @@ module JamRuby end end -end \ No newline at end of file +end diff --git a/spec/jam_ruby/connection_manager_spec.rb b/spec/jam_ruby/connection_manager_spec.rb index c4041e3db..c33198d03 100644 --- a/spec/jam_ruby/connection_manager_spec.rb +++ b/spec/jam_ruby/connection_manager_spec.rb @@ -70,6 +70,9 @@ describe ConnectionManager do result.getvalue(0, 0).should == "1" end + cc = Connection.find_by_client_id!(client_id) + cc.connected?.should be_true + @connman.delete_connection(client_id) @conn.exec("SELECT count(*) FROM connections where user_id = $1", [user_id]) do |result| @@ -171,23 +174,53 @@ describe ConnectionManager do @connman.gather_friends(@conn, user_id3).should == [user_id1] end - it "remove stale connection" do - + it "flag stale connection" do client_id = "client_id8" - user_id = create_user("test", "user8", "user8@jamkazam.com") - @connman.create_connection(user_id, client_id, "1.1.1.1") - @connman.remove_stale_connections(60) + num = JamRuby::Connection.count(:conditions => ['aasm_state = ?','connected']) + num.should == 1 + assert_num_connections(client_id, num) + @connman.flag_stale_connections(60) + assert_num_connections(client_id, num) + sleep(1) + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) + num.should == 1 + # this should change the aasm_state to stale + @connman.flag_stale_connections(1) + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'connected'"]) + num.should == 0 + + num = JamRuby::Connection.count(:conditions => ["updated_at < (NOW() - interval '#{1} second') AND aasm_state = 'stale'"]) + num.should == 1 + assert_num_connections(client_id, 1) + + @connman.expire_stale_connections(1) + sleep(1) + assert_num_connections(client_id, 0) + end + + it "expires stale connection" do + client_id = "client_id8" + user_id = create_user("test", "user8", "user8@jamkazam.com") + @connman.create_connection(user_id, client_id, "1.1.1.1") + + sleep(1) + @connman.flag_stale_connections(1) + assert_num_connections(client_id, 1) + # assert_num_connections(client_id, JamRuby::Connection.count(:conditions => ['aasm_state = ?','stale'])) + + @connman.expire_stale_connections(60) assert_num_connections(client_id, 1) sleep(1) - # this should remove the stale connection - @connman.remove_stale_connections(1) - + # this should delete the stale connection + @connman.expire_stale_connections(1) assert_num_connections(client_id, 0) end diff --git a/spec/jam_ruby/models/connection_spec.rb b/spec/jam_ruby/models/connection_spec.rb index 8b66ffc16..29d23f476 100644 --- a/spec/jam_ruby/models/connection_spec.rb +++ b/spec/jam_ruby/models/connection_spec.rb @@ -1,5 +1,36 @@ require 'spec_helper' describe Connection do + let(:user) { FactoryGirl.create(:user) } + let (:music_session) { FactoryGirl.create(:music_session, :creator => user) } -end \ No newline at end of file + it 'starts in the correct state' do + connection = FactoryGirl.create(:connection, + :user => user, + :music_session => music_session, + :ip_address => "1.1.1.1", + :client_id => "1") + + connection.idle?.should be_true + end + + it 'transitions properly' do + connection = FactoryGirl.create(:connection, + :user => user, + :music_session => music_session, + :ip_address => "1.1.1.1", + :client_id => "1") + + connection.connect! + connection.connected?.should be_true + connection.state_message.should == 'Connected' + + connection.stale! + connection.stale?.should be_true + connection.state_message.should == 'Stale' + + connection.expire! + connection.destroyed?.should be_true + end + +end