diff --git a/lib/sidekiq/grouping/redis.rb b/lib/sidekiq/grouping/redis.rb index 5ace9d6..cfa31b6 100644 --- a/lib/sidekiq/grouping/redis.rb +++ b/lib/sidekiq/grouping/redis.rb @@ -1,13 +1,15 @@ # frozen_string_literal: true -require_relative "./redis_dispatcher" +require_relative "redis_dispatcher" module Sidekiq module Grouping class Redis include RedisDispatcher - PLUCK_SCRIPT = <<-SCRIPT + BREAK_VERSION = "6.2.0" + + PLUCK_SCRIPT_GTE_6_2_0 = <<-SCRIPT local pluck_values = redis.call('lpop', KEYS[1], ARGV[1]) or {} if #pluck_values > 0 then redis.call('srem', KEYS[2], unpack(pluck_values)) @@ -15,6 +17,15 @@ class Redis return pluck_values SCRIPT + PLUCK_SCRIPT_LT_6_2_0 = <<-SCRIPT + local pluck_values = redis.call('lrange', KEYS[1], 0, ARGV[1] - 1) + redis.call('ltrim', KEYS[1], ARGV[1], -1) + for k, v in pairs(pluck_values) do + redis.call('srem', KEYS[2], v) + end + return pluck_values + SCRIPT + def push_msg(name, msg, remember_unique: false) redis do |conn| conn.multi do |pipeline| @@ -50,7 +61,7 @@ def pluck(name, limit) if new_redis_client? redis_call( :eval, - PLUCK_SCRIPT, + pluck_script, 2, ns(name), unique_messages_key(name), @@ -59,7 +70,7 @@ def pluck(name, limit) else keys = [ns(name), unique_messages_key(name)] args = [limit] - redis_call(:eval, PLUCK_SCRIPT, keys, args) + redis_call(:eval, pluck_script, keys, args) end end @@ -100,6 +111,26 @@ def unique_messages_key(name) def ns(key = nil) "batching:#{key}" end + + def server_version + Sidekiq.redis do |conn| + conn.info["redis_version"] + end + end + + # + # The optimized LUA SCRIPT works from Redis greater than or equal to 6.2. + # Check Redis version in use and return the suitable PLUCK_SCRIPT + # + # @return [] + # + def pluck_script + if Gem::Version.new(server_version) >= Gem::Version.new(BREAK_VERSION) + PLUCK_SCRIPT_GTE_6_2_0 + else + PLUCK_SCRIPT_LT_6_2_0 + end + end end end end diff --git a/spec/modules/redis_spec.rb b/spec/modules/redis_spec.rb index 2a121e4..a0569fd 100644 --- a/spec/modules/redis_spec.rb +++ b/spec/modules/redis_spec.rb @@ -41,4 +41,26 @@ expect(redis_call(:smembers, unique_key)).to eq [] end end + + describe "#pluck_script" do + context "when Redis server version is" do + it ">= 6.2.0, selects the corresponding pluck script" do + allow_any_instance_of(described_class) + .to receive(:server_version) + .and_return("6.2.0") + expect(redis_service.send(:pluck_script)).to eq( + described_class::PLUCK_SCRIPT_GTE_6_2_0 + ) + end + + it "< 6.2.0, selects the corresponding pluck script" do + allow_any_instance_of(described_class) + .to receive(:server_version) + .and_return("6.0.0") + expect(redis_service.send(:pluck_script)).to eq( + described_class::PLUCK_SCRIPT_LT_6_2_0 + ) + end + end + end end