Skip to content

Commit

Permalink
Add support for connection_pool gem (#215)
Browse files Browse the repository at this point in the history
* Add support for connection_pool gem

* Refactor ConnectionPool command send logic

* Add ConnectionPool calls assertions to specs
  • Loading branch information
danila committed Oct 27, 2022
1 parent 8543329 commit f7094ed
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 9 deletions.
24 changes: 16 additions & 8 deletions lib/redis/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,8 @@ def method_missing(command, *args, &block)
"passthrough has been deprecated and will be removed in " +
"redis-namespace 2.0 (at #{call_site})")
end
@redis.send(command, *args, &block)

wrapped_send(@redis, command, args, &block)
else
super
end
Expand Down Expand Up @@ -477,7 +478,7 @@ def call_with_namespace(command, *args, &block)
end

# Dispatch the command to Redis and store the result.
result = @redis.send(command, *args, &block)
result = wrapped_send(@redis, command, args, &block)

# Don't try to remove namespace from a Redis::Future, you can't.
return result if result.is_a?(Redis::Future)
Expand Down Expand Up @@ -514,6 +515,16 @@ def ruby2_keywords_hash(kwargs)
end
end

def wrapped_send(redis_client, command, args = [], &block)
if redis_client.class.name == "ConnectionPool"
redis_client.with do |pool_connection|
pool_connection.send(command, *args, &block)
end
else
redis_client.send(command, *args, &block)
end
end

# Avoid modifying the caller's (pass-by-reference) arguments.
def clone_args(arg)
if arg.is_a?(Array)
Expand All @@ -531,13 +542,10 @@ def call_site

def namespaced_block(command, &block)
if block.arity == 0
redis.send(command, &block)
wrapped_send(redis, command, &block)
else
redis.send(command) do |r|
copy = dup
copy.redis = r
yield copy
end
outer_block = proc { |r| copy = dup; copy.redis = r; yield copy }
wrapped_send(redis, command, &outer_block)
end
end

Expand Down
1 change: 1 addition & 0 deletions redis-namespace.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Gem::Specification.new do |s|
s.add_development_dependency "rake"
s.add_development_dependency "rspec", "~> 3.7"
s.add_development_dependency "rspec-its"
s.add_development_dependency "connection_pool"

s.description = <<description
Adds a Redis::Namespace class which can be used to namespace calls
Expand Down
2 changes: 1 addition & 1 deletion spec/deprecation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
end

before(:each) do
allow(redis).to receive(:unhandled) do |*args|
allow(redis).to receive(:unhandled) do |*args|
"unhandled(#{args.inspect})"
end
allow(redis).to receive(:flushdb).and_return("OK")
Expand Down
81 changes: 81 additions & 0 deletions spec/redis_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# encoding: utf-8

require File.dirname(__FILE__) + '/spec_helper'
require 'connection_pool'

describe "redis" do
@redis_version = Gem::Version.new(Redis.new.info["redis_version"])
Expand Down Expand Up @@ -184,6 +185,24 @@
expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({'foo'=>'1000', 'bar'=>'2000', 'baz' => nil})
end

it "should utilize connection_pool while using a namespace with mget" do
memo = @namespaced
connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
@namespaced = Redis::Namespace.new(:ns, redis: connection_pool)

expect(connection_pool).to receive(:with).and_call_original do |arg|
expect(arg).to be(an_instance_of(Redis))
end.at_least(:once)

@namespaced.set('foo', 1000)
@namespaced.set('bar', 2000)
expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000' })
expect(@namespaced.mapped_mget('foo', 'baz', 'bar')).to eq({'foo'=>'1000', 'bar'=>'2000', 'baz' => nil})
@redis.get('foo').should eq('bar')

@namespaced = memo
end

it "should be able to use a namespace with mset" do
@namespaced.mset('foo', '1000', 'bar', '2000')
expect(@namespaced.mapped_mget('foo', 'bar')).to eq({ 'foo' => '1000', 'bar' => '2000' })
Expand Down Expand Up @@ -376,6 +395,26 @@
expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
end

it "should utilize connection_pool while adding namepsace to multi blocks" do
memo = @namespaced
connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
@namespaced = Redis::Namespace.new(:ns, redis: connection_pool)

expect(connection_pool).to receive(:with).and_call_original do |arg|
expect(arg).to be(an_instance_of(Redis))
end.at_least(:once)

@namespaced.mapped_hmset "foo", {"key" => "value"}
@namespaced.multi do |r|
r.del "foo"
r.mapped_hmset "foo", {"key1" => "value1"}
end
expect(@redis.get("foo")).to eq("bar")
expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})

@namespaced = memo
end

it "should pass through multi commands without block" do
@namespaced.mapped_hmset "foo", {"key" => "value"}

Expand All @@ -387,6 +426,28 @@
expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
end

it "should utilize connection_pool while passing through multi commands without block" do
memo = @namespaced
connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
@namespaced = Redis::Namespace.new(:ns, redis: connection_pool)

expect(connection_pool).to receive(:with).and_call_original do |arg|
expect(arg).to be(an_instance_of(Redis))
end.at_least(:once)

@namespaced.mapped_hmset "foo", {"key" => "value"}

@namespaced.multi
@namespaced.del "foo"
@namespaced.mapped_hmset "foo", {"key1" => "value1"}
@namespaced.exec

expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
expect(@redis.get("foo")).to eq("bar")

@namespaced = memo
end

it 'should return futures without attempting to remove namespaces' do
@namespaced.multi do
@future = @namespaced.keys('*')
Expand All @@ -403,6 +464,26 @@
expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
end

it "should utilize connection_pool while adding namespace to pipelined blocks" do
memo = @namespaced
connection_pool = ConnectionPool.new(size: 2, timeout: 2) { Redis.new db: 15 }
@namespaced = Redis::Namespace.new(:ns, redis: connection_pool)

expect(connection_pool).to receive(:with).and_call_original do |arg|
expect(arg).to be(an_instance_of(Redis))
end.at_least(:once)

@namespaced.mapped_hmset "foo", {"key" => "value"}
@namespaced.pipelined do |r|
r.del "foo"
r.mapped_hmset "foo", {"key1" => "value1"}
end
expect(@namespaced.hgetall("foo")).to eq({"key1" => "value1"})
expect(@redis.get("foo")).to eq("bar")

@namespaced = memo
end

it "should returned response array from pipelined block" do
@namespaced.mset "foo", "bar", "key", "value"
result = @namespaced.pipelined do |r|
Expand Down

0 comments on commit f7094ed

Please sign in to comment.