User:Akumarm: Difference between revisions
No edit summary |
No edit summary |
||
Line 105: | Line 105: | ||
Key Takeaway: This function ensures every reviewer receives a topic without requiring manual intervention. | Key Takeaway: This function ensures every reviewer receives a topic without requiring manual intervention. | ||
=== ''RSpec Testing of Fallback Algorithm'' === | |||
To verify correctness, we use unit tests in ''review_bid_spec.rb'': | |||
describe '.fallback_algorithm' do | |||
describe '.fallback_algorithm' do | |||
let(:assignment_id) { 2085 } | let(:assignment_id) { 2085 } | ||
let(:reviewer_ids) { [1, 2, 3] } | let(:reviewer_ids) { [1, 2, 3] } | ||
let(:topics) { [101, 102, 103] } | let(:topics) { [101, 102, 103] } | ||
let(:teams) { { 101 => 5, 102 => 3, 103 => 1 } } | let(:teams) { { 101 => 5, 102 => 3, 103 => 1 } } | ||
before do | before do | ||
allow(SignUpTopic).to receive(:where).with(assignment_id: assignment_id).and_return(double(pluck: topics)) | allow(SignUpTopic).to receive(:where).with(assignment_id: assignment_id).and_return(double(pluck: topics)) | ||
Line 145: | Line 120: | ||
allow(SignedUpTeam).to receive(:topic_id).with(assignment_id, 3).and_return(nil) | allow(SignedUpTeam).to receive(:topic_id).with(assignment_id, 3).and_return(nil) | ||
end | end | ||
it 'assigns topics in a round-robin manner while avoiding self-assignment' do | it 'assigns topics in a round-robin manner while avoiding self-assignment' do | ||
result = ReviewBid.fallback_algorithm(assignment_id, reviewer_ids) | result = ReviewBid.fallback_algorithm(assignment_id, reviewer_ids) | ||
Line 152: | Line 126: | ||
expect([101, 102, 103]).to include(result['3'].first) | expect([101, 102, 103]).to include(result['3'].first) | ||
end | end | ||
end | end | ||
The Fallback Algorithm ensures that ''topics are assigned fairly'' even when the primary bidding service fails. By prioritizing ''larger teams'', ''avoiding self-review'', and using a ''round-robin strategy'', the fallback mechanism keeps the review process smooth and automatic. | |||
The |
Revision as of 21:19, 24 March 2025
Fallback Algorithm
Introduction
The Fallback Algorithm in Expertiza is designed to ensure automatic topic assignment when the primary Bidding Algorithm fails due to Web Service unavailability.
Normally, Expertiza allows reviewers to bid on topics, and the ReviewBiddingAlgorithmService processes these bids to assign topics. However, when this web service fails (due to API downtime, server issues, or unexpected errors), the system must switch to fallback and automatically assign topics to reviewers.
Solution
It is triggered when the web service is unavailable. It ensures fair topic assignment by:
- Prioritizing topics with the largest teams.
- Using a round-robin approach to distribute topics among reviewers.
- Ensuring reviewers do not get their own team’s topic.
When is this Used?
- The ReviewBiddingAlgorithmService fails due to an API issue.
- The web service times out or returns an error.
- Unexpected exceptions occur in the bidding process.
Instead of stopping the review process, the system automatically falls-back to an internal algorithm to distribute topics.
How Does the Fallback Algorithm Work?
The algorithm follows these **four main steps:
- Fetch available topics – Retrieves all topics for the given assignment.
- Sort topics by team size – Topics with more members are given priority.
- Create a topic queue – Topics are arranged in descending order of team size.
- Assign topics in a round-robin manner – Reviewers are assigned topics while avoiding their own team's topic.
This ensures fair and balanced distribution of topics.
Implementation - Controller Code (Triggering the Fallback)
If the primary bidding service fails, the system catches the error and calls the Fallback Algorithm.
def assign_bidding assignment_id = params[:assignment_id].to_i reviewer_ids = AssignmentParticipant.where(parent_id: assignment_id).ids begin # Attempt to use the web service bidding_data = ReviewBid.bidding_data(assignment_id, reviewer_ids) matched_topics = ReviewBiddingAlgorithmService.run_bidding_algorithm(bidding_data) rescue StandardError => e # If web service fails, trigger fallback Rails.logger.error "Web service unavailable: #{e.message}" matched_topics = ReviewBid.fallback_algorithm(assignment_id, reviewer_ids) end # Ensure valid topic assignments matched_topics ||= {} reviewer_ids.each { |reviewer_id| matched_topics[reviewer_id.to_s] ||= [] } Rails.logger.debug "Final matched topics after fallback: #{matched_topics.inspect}" # Assign topics to reviewers ReviewBid.assign_review_topics(assignment_id, reviewer_ids, matched_topics) # Disable topic selection after assignment Assignment.find(assignment_id).update(can_choose_topic_to_review: false) redirect_back fallback_location: root_path end
Key Takeaway: If ReviewBiddingAlgorithmService fails, the system automatically calls fallback_algorithm.
Implementation - Model Code (Fallback Algorithm)
This method ensures fair topic assignment when the bidding system fails.
def fallback_algorithm(assignment_id, reviewer_ids) Rails.logger.debug "Fallback algorithm triggered for assignment_id: #{assignment_id}" matched_topics = {} # Step 1: Get available topics topics = SignUpTopic.where(assignment_id: assignment_id).pluck(:id) Rails.logger.debug "Available topics: #{topics}" # Step 2: Get team sizes and sort by largest teams first teams = SignedUpTeam.where(topic_id: topics) .joins(:team) .joins("LEFT JOIN teams_users ON teams.id = teams_users.team_id") .group(:topic_id) .count("teams_users.user_id") # Sort teams by size (Descending Order) sorted_teams = teams.sort_by { |_, count| -count } Rails.logger.debug "Teams sorted by size: #{sorted_teams}" # Step 3: Create topic queue (largest teams first) topic_queue = sorted_teams.map(&:first) # Step 4: Assign topics in a round-robin manner topic_index = 0 reviewer_ids.each do |reviewer_id| assigned_topic = nil self_topic = fetch_self_topic(assignment_id, reviewer_id) attempts = 0 while assigned_topic.nil? && attempts < topic_queue.size topic_id = topic_queue[topic_index % topic_queue.size] unless topic_id == self_topic assigned_topic = topic_id Rails.logger.debug "Assigned topic #{assigned_topic} to reviewer #{reviewer_id}" topic_index += 1 end attempts += 1 end matched_topics[reviewer_id.to_s] = assigned_topic ? [assigned_topic] : [] end Rails.logger.debug "Final matched topics after fallback: #{matched_topics.inspect}" matched_topics end
Key Takeaway: This function ensures every reviewer receives a topic without requiring manual intervention.
RSpec Testing of Fallback Algorithm
To verify correctness, we use unit tests in review_bid_spec.rb:
describe '.fallback_algorithm' do let(:assignment_id) { 2085 } let(:reviewer_ids) { [1, 2, 3] } let(:topics) { [101, 102, 103] } let(:teams) { { 101 => 5, 102 => 3, 103 => 1 } } before do allow(SignUpTopic).to receive(:where).with(assignment_id: assignment_id).and_return(double(pluck: topics)) allow(SignedUpTeam).to receive_message_chain(:where, :joins, :group, :count).and_return(teams) allow(SignedUpTeam).to receive(:topic_id).with(assignment_id, 1).and_return(101) allow(SignedUpTeam).to receive(:topic_id).with(assignment_id, 2).and_return(102) allow(SignedUpTeam).to receive(:topic_id).with(assignment_id, 3).and_return(nil) end it 'assigns topics in a round-robin manner while avoiding self-assignment' do result = ReviewBid.fallback_algorithm(assignment_id, reviewer_ids) expect(result['1']).not_to include(101) expect(result['2']).not_to include(102) expect([101, 102, 103]).to include(result['3'].first) end end
The Fallback Algorithm ensures that topics are assigned fairly even when the primary bidding service fails. By prioritizing larger teams, avoiding self-review, and using a round-robin strategy, the fallback mechanism keeps the review process smooth and automatic.