Skip to content

Commit

Permalink
reduce number of calls to redis by using more pipelining where possib…
Browse files Browse the repository at this point in the history
…le and hmget in place of hget when it makes sense

  - add method members_data_for and members_data_for_in which use hmget to get the metadata for multiple members in a single call to redis
  - add method rankings_for_members_having_scores and rankings_for_members_having_scores_in to pipeline gathering a list of ranks for members
  - make use of those methods in the methods that would benefit
  • Loading branch information
erichummel committed Jun 9, 2016
1 parent 2fdb47c commit 00b36ad
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 18 deletions.
59 changes: 49 additions & 10 deletions lib/competition_ranking_leaderboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,29 +67,36 @@ def ranked_in_list_in(leaderboard_name, members, options = {})
end
end unless leaderboard_options[:members_only]

included_members = []
scores = []

members.each_with_index do |member, index|
data = {}
data[@member_key] = member
unless leaderboard_options[:members_only]
data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1]

if data[@score_key] == nil
next unless leaderboard_options[:include_missing]
end

if @reverse
data[@rank_key] = @redis_connection.zcount(leaderboard_name, '-inf', "(#{data[@score_key]}") + 1 rescue nil
else
data[@rank_key] = @redis_connection.zcount(leaderboard_name, "(#{data[@score_key]}", '+inf') + 1 rescue nil
end
end

if leaderboard_options[:with_member_data]
data[@member_data_key] = member_data_for_in(leaderboard_name, member)
end
included_members << member
scores << data[@score_key]

ranks_for_members << data
end

if leaderboard_options[:with_member_data]
members_data_for_in(leaderboard_name, included_members).each_with_index do |member_data, index|
ranks_for_members[index][@member_data_key] = member_data
end
end

rankings_for_members_having_scores_in(leaderboard_name, included_members, scores).each_with_index do |rank, index|
ranks_for_members[index][@rank_key] = rank
end

case leaderboard_options[:sort_by]
when :rank
ranks_for_members = ranks_for_members.sort_by { |member| member[@rank_key] }
Expand All @@ -99,4 +106,36 @@ def ranked_in_list_in(leaderboard_name, members, options = {})

ranks_for_members
end
end

# Retrieve a list of the rankings of leaders given their member names and scores
#
# @param members [Array] Member names.
# @param scores [Array] a list of scores for the members, aligned with the member names
#
# @return a list of the rankings for the passed in members and scores

def rankings_for_members_having_scores(members, scores)
rankings_for_members_in(@leaderboard_name, members, scores)
end

# Retrieve a list of the rankings of leaders given their member names and scores
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param members [Array] Member names.
# @param scores [Array] a list of scores for the members, aligned with the member names
#
# @return a list of the rankings for the passed in members and scores

def rankings_for_members_having_scores_in(leaderboard_name, members, scores)
@redis_connection.multi do |transaction|
members.each_with_index do |member, index|
if @reverse
transaction.zcount(leaderboard_name, '-inf', "(#{scores[index]}")
else
transaction.zcount(leaderboard_name, "(#{scores[index]}", '+inf')
end
end
end.map{|rank| rank ? rank + 1 : rank}
end

end
29 changes: 25 additions & 4 deletions lib/leaderboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,25 @@ def member_data_for_in(leaderboard_name, member)
@redis_connection.hget(member_data_key(leaderboard_name), member)
end

# Retrieve the optional member data for a given member in the named leaderboard.
#
# @param leaderboard_name [String] Name of the leaderboard.
# @param members [Array] Member names.
#
# @return array of strings of optional member data.
def members_data_for_in(leaderboard_name, members)
@redis_connection.hmget(member_data_key(leaderboard_name), *members)
end

# Retrieve the optional member data for the given members in the leaderboard.
#
# @param members [Array] Member names.
#
# @return array of strings of optional member data.
def members_data_for(members)
members_data_for_in(@leaderboard_name, members)
end

# Update the optional member data for a given member in the leaderboard.
#
# @param member [String] Member name.
Expand Down Expand Up @@ -964,13 +983,15 @@ def ranked_in_list_in(leaderboard_name, members, options = {})
data[@score_key] = responses[index * 2 + 1].to_f if responses[index * 2 + 1]
end

if leaderboard_options[:with_member_data]
data[@member_data_key] = member_data_for_in(leaderboard_name, member)
end

ranks_for_members << data
end

if leaderboard_options[:with_member_data]
members_data_for_in(leaderboard_name, members).each_with_index do |member_data, index|
ranks_for_members[index][@member_data_key] = member_data
end
end

case leaderboard_options[:sort_by]
when :rank
ranks_for_members = ranks_for_members.sort_by { |member| member[@rank_key] }
Expand Down
10 changes: 6 additions & 4 deletions lib/tie_ranking_leaderboard.rb
Original file line number Diff line number Diff line change
Expand Up @@ -249,13 +249,15 @@ def ranked_in_list_in(leaderboard_name, members, options = {})
end
end

if leaderboard_options[:with_member_data]
data[@member_data_key] = member_data_for_in(leaderboard_name, member)
end

ranks_for_members << data
end

if leaderboard_options[:with_member_data]
members_data_for_in(leaderboard_name, members).each_with_index do |member_data, index|
ranks_for_members[index][@member_data_key] = member_data
end
end

case leaderboard_options[:sort_by]
when :rank
ranks_for_members = ranks_for_members.sort_by { |member| member[@rank_key] }
Expand Down

0 comments on commit 00b36ad

Please sign in to comment.