Skip to content

Commit 14eeb53

Browse files
committed
Fix edge case where meeting can be recreated on cordoned server
1 parent 4d4718f commit 14eeb53

2 files changed

Lines changed: 55 additions & 0 deletions

File tree

app/controllers/bigbluebutton_api_controller.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,14 @@ def create
171171
# Check if meeting is already running
172172
meeting = Meeting.find(params[:meetingID], @tenant&.id)
173173
server = meeting.server
174+
175+
# Handle edge case where stale meeting persists on cordoned server
176+
if server.cordoned? && !meeting_running_on_server?(meeting)
177+
logger.info("Meeting #{params[:meetingID]} no longer running on cordoned server #{server.id}; re-allocating.")
178+
meeting.destroy!
179+
raise ApplicationRedisRecord::RecordNotFound.new('stale meeting on cordoned server', 'Meeting', params[:meetingID])
180+
end
181+
174182
logger.debug("Found existing meeting #{params[:meetingID]} on BigBlueButton server #{server.id}.")
175183
rescue ApplicationRedisRecord::RecordNotFound
176184
begin
@@ -572,6 +580,16 @@ def pass_through_params(excluded_params)
572580
.to_unsafe_hash
573581
end
574582

583+
def meeting_running_on_server?(meeting)
584+
server = meeting.server
585+
uri = encode_bbb_uri('isMeetingRunning', server.url, server.secret, 'meetingID' => meeting.id)
586+
response = get_post_req(uri, **bbb_req_timeout(server))
587+
response.at_xpath('/response/running')&.text == 'true'
588+
rescue StandardError => e
589+
logger.warn("Could not verify meeting #{meeting.id} on cordoned server #{server.id}: #{e}")
590+
false
591+
end
592+
575593
# Success response if there are no meetings on any servers
576594
def no_meetings_response
577595
Nokogiri::XML::Builder.new do |xml|

spec/requests/bigbluebutton_api_controller_spec.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,43 @@
538538
expect(new_server.load).to eq(1)
539539
end
540540

541+
context 'when the meeting is pinned to a cordoned server' do
542+
let!(:cordoned_server) do
543+
s = create(:server)
544+
s.state = 'cordoned'
545+
s.save!
546+
s
547+
end
548+
549+
it 're-allocates to an available server when the meeting is no longer running there' do
550+
Meeting.find_or_create_with_server!('test-meeting-1', cordoned_server, 'mp')
551+
552+
stub_request(:get, /#{Regexp.escape(cordoned_server.url)}isMeetingRunning/)
553+
.to_return(body: '<response><returncode>SUCCESS</returncode><running>false</running></response>')
554+
stub_create = stub_request(:get, /#{Regexp.escape(server.url)}create/)
555+
.to_return(body: '<response><returncode>SUCCESS</returncode><meetingID>test-meeting-1</meetingID></response>')
556+
557+
get bigbluebutton_api_create_url, params: { meetingID: 'test-meeting-1', moderatorPW: 'mp', voiceBridge: '1234567' }
558+
559+
expect(stub_create).to have_been_requested
560+
expect(Meeting.find('test-meeting-1').server.id).to eq(server.id)
561+
end
562+
563+
it 'keeps the meeting on the cordoned server when it is still running there' do
564+
Meeting.find_or_create_with_server!('test-meeting-1', cordoned_server, 'mp')
565+
566+
stub_request(:get, /#{Regexp.escape(cordoned_server.url)}isMeetingRunning/)
567+
.to_return(body: '<response><returncode>SUCCESS</returncode><running>true</running></response>')
568+
stub_create = stub_request(:get, /#{Regexp.escape(cordoned_server.url)}create/)
569+
.to_return(body: '<response><returncode>SUCCESS</returncode><meetingID>test-meeting-1</meetingID></response>')
570+
571+
get bigbluebutton_api_create_url, params: { meetingID: 'test-meeting-1', moderatorPW: 'mp', voiceBridge: '1234567' }
572+
573+
expect(stub_create).to have_been_requested
574+
expect(Meeting.find('test-meeting-1').server.id).to eq(cordoned_server.id)
575+
end
576+
end
577+
541578
it "returns an appropriate error on timeout" do
542579
params = { meetingID: "test-meeting-1", moderatorPW: "mp", voiceBridge: "1234567" }
543580

0 commit comments

Comments
 (0)