resque / redis-namespace

This gem adds a Redis::Namespace class which can be used to namespace Redis keys.
http://redis.io
MIT License
695 stars 192 forks source link

Fix compatibility with redis-rb 4.6.0 #192

Closed casperisfine closed 2 years ago

casperisfine commented 2 years ago

Fix: https://github.com/resque/redis-namespace/issues/191 Fix: https://github.com/redis/redis-rb/issues/1069 Fix: https://github.com/redis/redis-rb/issues/1088

In redis-rb 4.6.0, the object yielded by multi and pipelined is an unsynchronized PipelinedConnection object.

This changed opened a thread safety issue in Redis::Namespace:

cc @ArturT, @sergio91pt (extra testing welcome)

ArturT commented 2 years ago

Thank you @casperisfine . I've tested your fix locally and it does solve the problem.

# Gemfile
source 'https://rubygems.org'

gem 'redis', path: './redis-rb'
#gem 'redis', '4.5.1'
gem 'redis-namespace', path: './redis-namespace' # local copy of this PR branch
#gem 'redis-namespace'
# reproduce_bug.rb
require 'bundler/setup'
require 'redis'
require 'redis-namespace'

puts "Redis gem version: #{Redis::VERSION}"

key = 'a_key'
threads = []
redis = Redis.new(timeout: 5)
redis = Redis::Namespace.new('my-namespace', redis: redis)

index = 0

while true do
  100.times do
    threads << Thread.new do
      100.times do
        redis.multi do |transaction|
          transaction.incr(key)
          transaction.expire(key, 360)
        end
      end
    end
  end

  threads.each(&:join)

  puts "It works! Iteration: #{index}"
  index += 1
end

Output:

$ ruby reproduce_bug.rb

Redis gem version: 4.6.0
It works! Iteration: 0
It works! Iteration: 1
It works! Iteration: 2
...
It works! Iteration: 1498