Closed casperisfine closed 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
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
andpipelined
is an unsynchronizedPipelinedConnection
object.This changed opened a thread safety issue in Redis::Namespace:
redis.multi do
, setsNamespace#redis = PipelinedConnection
multi
block is called on the pipelined connectioncc @ArturT, @sergio91pt (extra testing welcome)