Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for multiple redis connections #49

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
35 changes: 25 additions & 10 deletions lib/modis.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,20 @@

module Modis
@mutex = Mutex.new

class << self
attr_writer :redis_options, :connection_pool_size, :connection_pool_timeout,
:connection_pool
:connection_pools

def redis_options
@redis_options ||= {}
@redis_options ||= { default: {} }
end

def redis_options=(options)
if options.is_a?(Hash) && options.values.first.is_a?(Hash)
@redis_options = options.transform_values(&:dup)
else
@redis_options[:default] = options
end
end

def connection_pool_size
Expand All @@ -35,17 +42,25 @@ def connection_pool_timeout
@connection_pool_timeout ||= 5
end

def connection_pool
return @connection_pool if @connection_pool
def connection_pools
@connection_pools ||= {}
end

@mutex.synchronize do
options = { size: connection_pool_size, timeout: connection_pool_timeout }
@connection_pool = ConnectionPool.new(options) { Redis.new(redis_options) }
def connection_pool(pool_name = :default)
connection_pools[pool_name] ||= begin
@mutex.synchronize do
ConnectionPool.new(
size: connection_pool_size,
timeout: connection_pool_timeout
) do
Redis.new(redis_options[pool_name])
end
end
end
end

def with_connection
connection_pool.with { |connection| yield(connection) }
def with_connection(pool_name = :default)
connection_pool(pool_name).with { |connection| yield(connection) }
end
end
end
4 changes: 2 additions & 2 deletions lib/modis/finder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def all
"because you disabled all index. See :enable_all_index for more."
end

records = Modis.with_connection do |redis|
records = Modis.with_connection(self.modis_connection) do |redis|
ids = redis.smembers(key_for(:all))
redis.pipelined do |pipeline|
ids.map { |id| record_for(pipeline, id) }
Expand All @@ -41,7 +41,7 @@ def attributes_for(redis, id)
def find_all(ids)
raise RecordNotFound, "Couldn't find #{name} without an ID" if ids.empty?

records = Modis.with_connection do |redis|
records = Modis.with_connection(self.modis_connection) do |redis|
if ids.count == 1
ids.map { |id| record_for(redis, id) }
else
Expand Down
2 changes: 1 addition & 1 deletion lib/modis/index.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def where(query)
end

def index_for(attribute, value)
Modis.with_connection do |redis|
Modis.with_connection(self.modis_connection) do |redis|
key = index_key(attribute, value)
redis.smembers(key).map(&:to_i)
end
Expand Down
2 changes: 2 additions & 0 deletions lib/modis/model.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ def self.included(base)
include Modis::Index

base.extend(ClassMethods)
base.class_attribute :modis_connection
base.modis_connection = :default
end
end

Expand Down
4 changes: 2 additions & 2 deletions lib/modis/persistence.rb
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def destroy
end

def reload
new_attributes = Modis.with_connection { |redis| self.class.attributes_for(redis, id) }
new_attributes = Modis.with_connection(self.modis_connection) { |redis| self.class.attributes_for(redis, id) }
initialize(new_attributes)
self
end
Expand Down Expand Up @@ -253,7 +253,7 @@ def coerced_attributes

def set_id
namespace = self.class.sti_child? ? self.class.sti_base_absolute_namespace : self.class.absolute_namespace
Modis.with_connection do |redis|
Modis.with_connection(self.modis_connection) do |redis|
self.id = redis.incr("#{namespace}_id_seq")
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/modis/transaction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ def self.included(base)

module ClassMethods
def transaction
Modis.with_connection { |redis| redis.multi { |transaction| yield(transaction) } }
Modis.with_connection(self.modis_connection) { |redis| redis.multi { |transaction| yield(transaction) } }
end
end
end
Expand Down
54 changes: 54 additions & 0 deletions spec/multi_redis_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module MultiRedisSpec
class DefaultUserModel
include Modis::Model

attribute :name, :string
end

class CustomUserModel
include Modis::Model
self.modis_connection = :custom

attribute :name, :string
end
end

describe 'Multiple redis support' do
before do
Modis.redis_options = {
default: { url: 'redis://localhost:6379/0' },
custom: { url: 'redis://localhost:6379/1' }
}
end

it 'uses the default redis connection' do
expect(Modis).to receive(:with_connection).with(:default).at_least(3).times.and_call_original
user = MultiRedisSpec::DefaultUserModel.create!(name: 'Ian')

expect(Modis).to receive(:with_connection).with(:default).at_least(3).times.and_call_original
MultiRedisSpec::DefaultUserModel.find(user.id)
end

it 'uses the specified redis connection when set up' do
expect(Modis).to receive(:with_connection).with(:custom).at_least(3).times.and_call_original
user = MultiRedisSpec::CustomUserModel.create!(name: 'Tanya')

expect(Modis).to receive(:with_connection).with(:custom).at_least(3).times.and_call_original
MultiRedisSpec::CustomUserModel.find(user.id)
end
end

describe 'backwards compatibility' do
before do
Modis.redis_options = {
url: 'redis://localhost:6379/0'
}
end

it 'uses the default redis connection' do
expect(Modis).to receive(:with_connection).with(:default).at_least(3).times.and_call_original
MultiRedisSpec::DefaultUserModel.create!(name: 'Ian')
end
end
9 changes: 6 additions & 3 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@

RSpec.configure do |config|
config.after :each do
Modis.with_connection do |connection|
keys = connection.keys "#{Modis.config.namespace}:*"
connection.del(*keys) unless keys.empty?
RSpec::Mocks.space.proxy_for(Modis).reset
Modis.connection_pools.each do |key, _|
Modis.with_connection(key) do |connection|
keys = connection.keys "#{Modis.config.namespace}:*"
connection.del(*keys) unless keys.empty?
end
end
end
end