From d3e787fb2adbb237eae538ac76f8cb74f8e5eaef Mon Sep 17 00:00:00 2001 From: Seth Call Date: Sun, 8 Oct 2017 10:43:33 -0500 Subject: [PATCH] optimize session query and catch dead connection in websocket gateway; heal dbconn --- db/manifest | 3 +- db/up/sms_index_optimize.sql | 206 ++++++++++++++++++ .../lib/jam_websockets/router.rb | 5 + 3 files changed, 213 insertions(+), 1 deletion(-) create mode 100644 db/up/sms_index_optimize.sql diff --git a/db/manifest b/db/manifest index 37a272648..08b38d4d1 100755 --- a/db/manifest +++ b/db/manifest @@ -376,4 +376,5 @@ jam_track_download_rights.sql guitar_center_integration_v1.sql mobile_recording_support.sql youtube_broadcast.sql -amazon_v1.sql \ No newline at end of file +amazon_v1.sql +sms_index_optimize.sql \ No newline at end of file diff --git a/db/up/sms_index_optimize.sql b/db/up/sms_index_optimize.sql new file mode 100644 index 000000000..65c53b771 --- /dev/null +++ b/db/up/sms_index_optimize.sql @@ -0,0 +1,206 @@ +-- check that the music_sessions does not currently have an active_music_sessions +CREATE OR REPLACE FUNCTION sms_index (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER, session_id VARCHAR, include_pending BOOLEAN DEFAULT FALSE) RETURNS VOID STRICT VOLATILE AS $$ + BEGIN + -- output table to hold tagged music sessions with latency + CREATE TEMPORARY TABLE sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP; + + IF session_id = 'any' THEN + -- populate sms_music_session_tmp as all music sessions + -- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step + INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM music_sessions + WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) + AND canceled = FALSE AND description != 'Jam Track Session' + AND id NOT IN (SELECT id FROM active_music_sessions); + + -- tag accepted rsvp as 1 + UPDATE sms_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE + q.music_session_id = s.music_session_id AND + s.id = rrs.rsvp_slot_id AND + rrs.rsvp_request_id = r.id AND + r.user_id = my_user_id AND + rrs.chosen = TRUE AND + q.tag is NULL; + + -- tag invitation as 2 + UPDATE sms_music_session_tmp q SET tag = 2 FROM invitations i WHERE + q.music_session_id = i.music_session_id AND + i.receiver_id = my_user_id AND + q.tag IS NULL; + + -- musician access as 3 + UPDATE sms_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE + q.music_session_id = m.id AND + m.open_rsvps = TRUE AND + q.tag IS NULL; + + -- delete anything not tagged + DELETE FROM sms_music_session_tmp WHERE tag IS NULL; + + ELSE + INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM music_sessions + WHERE music_sessions.id = session_id; + END IF; + + -- output table to hold users involved in the sms_music_session_tmp sessions and their latency + CREATE TEMPORARY TABLE sms_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, full_score INTEGER, audio_latency INTEGER, internet_score INTEGER) ON COMMIT DROP; + + IF my_audio_latency > -1 THEN + + IF include_pending THEN + -- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session + INSERT INTO sms_users_tmp SELECT DISTINCT q.music_session_id, users.id, s.full_score AS full_score, s.a_audio_latency, s.score + FROM sms_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id + LEFT OUTER JOIN current_scores s ON s.a_userid = users.id + WHERE + s.b_userid = my_user_id; + + -- populate sms_users_tmp with invited users for session in the sms_music_session_tmp table, accompanied with full latency and music session + -- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later + INSERT INTO sms_users_tmp SELECT NULL, users.id, s.full_score AS full_score, s.a_audio_latency, s.score + FROM sms_music_session_tmp q + INNER JOIN invitations ON invitations.music_session_id = q.music_session_id + INNER JOIN users ON invitations.receiver_id = users.id + LEFT OUTER JOIN current_scores s ON s.a_userid = users.id + WHERE + s.b_userid = my_user_id AND + users.id NOT IN (SELECT user_id FROM sms_users_tmp); + ELSE + -- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session + INSERT INTO sms_users_tmp SELECT DISTINCT q.music_session_id, users.id, s.full_score AS full_score, s.a_audio_latency, s.score + FROM sms_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id + LEFT OUTER JOIN current_scores s ON s.a_userid = users.id + WHERE + s.b_userid = my_user_id AND + rsvp_requests_rsvp_slots.chosen = TRUE AND + rsvp_requests.canceled != TRUE; + END IF; + END IF; + + -- calculate the average latency + UPDATE sms_music_session_tmp q SET latency = (select AVG(u.full_score) FROM sms_users_tmp u WHERE + q.music_session_id = u.music_session_id); + + RETURN; + END; +$$ LANGUAGE plpgsql; + +-- check that the music_sessions does not currently have an active_music_sessions +CREATE OR REPLACE FUNCTION sms_index_test (my_user_id VARCHAR, my_locidispid BIGINT, my_audio_latency INTEGER, session_id VARCHAR, include_pending BOOLEAN DEFAULT FALSE) RETURNS VOID STRICT VOLATILE AS $$ + BEGIN + -- output table to hold tagged music sessions with latency + CREATE TEMPORARY TABLE sms_music_session_tmp (music_session_id VARCHAR(64) NOT NULL, tag INTEGER, latency INTEGER) ON COMMIT DROP; + + IF session_id = 'any' THEN + -- populate sms_music_session_tmp as all music sessions + -- XXX: we should pass in enough info to match pagination/query to reduce the impact of this step + INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM music_sessions + WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) + AND canceled = FALSE AND description != 'Jam Track Session' + AND id NOT IN (SELECT id FROM active_music_sessions) ; + + -- tag accepted rsvp as 1 + UPDATE sms_music_session_tmp q SET tag = 1 FROM rsvp_slots s, rsvp_requests_rsvp_slots rrs, rsvp_requests r WHERE + q.music_session_id = s.music_session_id AND + s.id = rrs.rsvp_slot_id AND + rrs.rsvp_request_id = r.id AND + r.user_id = my_user_id AND + rrs.chosen = TRUE AND + q.tag is NULL; + + -- tag invitation as 2 + UPDATE sms_music_session_tmp q SET tag = 2 FROM invitations i WHERE + q.music_session_id = i.music_session_id AND + i.receiver_id = my_user_id AND + q.tag IS NULL; + + -- musician access as 3 + UPDATE sms_music_session_tmp q SET tag = 3 FROM music_sessions m WHERE + q.music_session_id = m.id AND + m.open_rsvps = TRUE AND + q.tag IS NULL; + + -- delete anything not tagged + DELETE FROM sms_music_session_tmp WHERE tag IS NULL; + + ELSE + INSERT INTO sms_music_session_tmp SELECT DISTINCT id, NULL::INTEGER AS tag, NULL::INTEGER AS latency + FROM music_sessions + WHERE music_sessions.id = session_id; + END IF; + + -- output table to hold users involved in the sms_music_session_tmp sessions and their latency + CREATE TEMPORARY TABLE sms_users_tmp (music_session_id VARCHAR(64), user_id VARCHAR(64) NOT NULL, full_score INTEGER, audio_latency INTEGER, internet_score INTEGER) ON COMMIT DROP; + + IF my_audio_latency > -1 THEN + + IF include_pending THEN + -- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session + INSERT INTO sms_users_tmp SELECT DISTINCT q.music_session_id, users.id, CAST(NULL AS INTEGER), CAST(NULL AS INTEGER), CAST(NULL AS INTEGER) + FROM sms_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id; + + -- populate sms_users_tmp with invited users for session in the sms_music_session_tmp table, accompanied with full latency and music session + -- specify NULL for music_session_id, because we don't want RSVP users to affect the AVG computed for each session later + INSERT INTO sms_users_tmp SELECT NULL, users.id, CAST(NULL AS INTEGER), CAST(NULL AS INTEGER), CAST(NULL AS INTEGER) + FROM sms_music_session_tmp q + INNER JOIN invitations ON invitations.music_session_id = q.music_session_id + INNER JOIN users ON invitations.receiver_id = users.id + WHERE + users.id NOT IN (SELECT user_id FROM sms_users_tmp); + ELSE + -- populate sms_users_tmp with users that have an approved RSVP for sessions in the sms_music_session_tmp table, accompanied with full latency and music session + INSERT INTO sms_users_tmp SELECT DISTINCT q.music_session_id, users.id, CAST(NULL AS INTEGER), CAST(NULL AS INTEGER), CAST(NULL AS INTEGER) + FROM sms_music_session_tmp q + INNER JOIN rsvp_slots ON rsvp_slots.music_session_id = q.music_session_id + INNER JOIN rsvp_requests_rsvp_slots ON rsvp_requests_rsvp_slots.rsvp_slot_id = rsvp_slots.id + INNER JOIN rsvp_requests ON rsvp_requests.id = rsvp_requests_rsvp_slots.rsvp_request_id + INNER JOIN users ON rsvp_requests.user_id = users.id + WHERE + rsvp_requests_rsvp_slots.chosen = TRUE AND + rsvp_requests.canceled != TRUE; + END IF; + END IF; + + -- calculate the average latency + --UPDATE sms_music_session_tmp q SET latency = (select AVG(u.full_score) FROM sms_users_tmp u WHERE + -- q.music_session_id = u.music_session_id); + + RETURN; + END; +$$ LANGUAGE plpgsql; + +-- XXXX TODO: TURN THESE ON AFTER PRODUCTION IS UPDATED +-- CREATE INDEX index_rsvp_requests_rsvp_slots_on_rsvp_request_id ON rsvp_requests_rsvp_slots USING btree(rsvp_request_id); +-- CREATE INDEX index_rsvp_requests_rsvp_slots_on_rsvp_slot_id ON rsvp_requests_rsvp_slots USING btree(rsvp_slot_id); +-- CREATE INDEX index_rsvp_requests_rsvp_slots_on_chosen ON rsvp_requests_rsvp_slots USING btree(chosen); +-- CREATE INDEX index_rsvp_slots_on_music_session_id ON rsvp_slots USING btree(music_session_id); +-- CREATE INDEX index_rsvp_requests_user_id ON rsvp_requests USING btree(user_id); +-- CREATE INDEX index_rsvp_requests_canceled ON rsvp_requests USING btree(canceled); +-- CREATE INDEX index_invitations_on_receiver_id ON invitations USING btree(receiver_id); +-- CREATE INDEX index_invitations_on_music_session_id ON invitations USING btree(music_session_id); +-- select sms_index_test('062deeba-b917-46e2-bfa3-e829405ca602'::varchar, 26880045373::bigint, 18.911563873291::integer, 'any'::varchar, false::boolean) + + + +-- update music_sessions set canceled = true WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) AND id NOT IN (select distinct on(name, user_id) id FROM music_sessions WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) order by name, user_id); + + + +-- select distinct on(name, user_id) name, description, scheduled_start FROM music_sessions WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) order by name, user_id; + +-- get count +-- SELECT count(id) FROM music_sessions WHERE (scheduled_start IS NULL OR scheduled_start > (NOW() - (interval '15 minute'))) AND canceled = FALSE AND description != 'Jam Track Session' AND id NOT IN (SELECT id FROM active_music_sessions) \ No newline at end of file diff --git a/websocket-gateway/lib/jam_websockets/router.rb b/websocket-gateway/lib/jam_websockets/router.rb index a2e329286..b7d30fe20 100644 --- a/websocket-gateway/lib/jam_websockets/router.rb +++ b/websocket-gateway/lib/jam_websockets/router.rb @@ -412,6 +412,11 @@ module JamWebsockets @log.error "ending client session due to server programming or runtime error. reason=#{e.to_s}" @log.error e + if PG::UnableToSend + # indicates connection to server is down; kill self. Will be restarted; if db is up we will be healthy + @log.error "EXITING DUE TO DEAD DBCONN" + Kernel.exit!(1) + end begin # wrap the message up and send it down error_msg = @message_factory.server_generic_error(e.to_s)