From 00b36adb3a5c925142cc58e5ebcab75d602c8843 Mon Sep 17 00:00:00 2001 From: Eric Hummel Date: Wed, 8 Jun 2016 12:30:10 -0400 Subject: [PATCH] reduce number of calls to redis by using more pipelining where possible 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 --- lib/competition_ranking_leaderboard.rb | 59 +++++++++++++++++++++----- lib/leaderboard.rb | 29 +++++++++++-- lib/tie_ranking_leaderboard.rb | 10 +++-- 3 files changed, 80 insertions(+), 18 deletions(-) diff --git a/lib/competition_ranking_leaderboard.rb b/lib/competition_ranking_leaderboard.rb index 4e10905..c76e8be 100644 --- a/lib/competition_ranking_leaderboard.rb +++ b/lib/competition_ranking_leaderboard.rb @@ -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] } @@ -99,4 +106,36 @@ def ranked_in_list_in(leaderboard_name, members, options = {}) ranks_for_members end -end \ No newline at end of file + + # 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 diff --git a/lib/leaderboard.rb b/lib/leaderboard.rb index 66efd81..a5f2ed7 100644 --- a/lib/leaderboard.rb +++ b/lib/leaderboard.rb @@ -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. @@ -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] } diff --git a/lib/tie_ranking_leaderboard.rb b/lib/tie_ranking_leaderboard.rb index 336ef3f..aa7b037 100644 --- a/lib/tie_ranking_leaderboard.rb +++ b/lib/tie_ranking_leaderboard.rb @@ -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] }