From 77d380dd6b31b24b6f837f31cfba0ea3ac0c1b01 Mon Sep 17 00:00:00 2001 From: Abhishek Date: Mon, 5 Oct 2020 20:01:10 +0800 Subject: [PATCH] chore: refactor redis config (#1310) - refactor Redis config in Redis::Config Module - unit tests for Redis::Config module --- config/initializers/redis.rb | 21 +---------- config/initializers/sidekiq.rb | 9 ++--- lib/redis/config.rb | 48 +++++++++++++++++++++++++ spec/lib/redis/config_spec.rb | 65 ++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 27 deletions(-) create mode 100644 lib/redis/config.rb create mode 100644 spec/lib/redis/config_spec.rb diff --git a/config/initializers/redis.rb b/config/initializers/redis.rb index 35739a1ab..d1aa25b2e 100644 --- a/config/initializers/redis.rb +++ b/config/initializers/redis.rb @@ -1,24 +1,5 @@ -app_redis_config = { - url: URI.parse(ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379')), - password: ENV.fetch('REDIS_PASSWORD', nil).presence -} +redis = Rails.env.test? ? MockRedis.new : Redis.new(Redis::Config.app) -if ENV['REDIS_SENTINELS'].presence - default_sentinel_port = '26379' - # expected format for REDIS_SENTINELS url string is host1:port1, host2:port2 - sentinels = ENV['REDIS_SENTINELS'].split(',').map do |sentinel_url| - host, port = sentinel_url.split(':').map(&:strip) - { host: host, port: port || default_sentinel_port, password: app_redis_config[:password] } - end - - master_name = ENV.fetch('REDIS_SENTINEL_MASTER_NAME', 'mymaster') - # over-write redis url as redis://:@/ when using sentinel - # more at https://github.com/redis/redis-rb/issues/531#issuecomment-263501322 - app_redis_config[:url] = URI.parse("redis://#{master_name}") - app_redis_config[:sentinels] = sentinels -end - -redis = Rails.env.test? ? MockRedis.new : Redis.new(app_redis_config) # Alfred # Add here as you use it for more features # Used for Round Robin, Conversation Emails & Online Presence diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index 8b535874e..7a28ca972 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,12 +1,7 @@ -sidekiq_redis_config = { - url: ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379'), - password: ENV.fetch('REDIS_PASSWORD', nil).presence -} - Sidekiq.configure_client do |config| - config.redis = sidekiq_redis_config.merge(size: 25) + config.redis = Redis::Config.sidekiq end Sidekiq.configure_server do |config| - config.redis = sidekiq_redis_config.merge(size: 25) + config.redis = Redis::Config.sidekiq end diff --git a/lib/redis/config.rb b/lib/redis/config.rb new file mode 100644 index 000000000..756b4bbee --- /dev/null +++ b/lib/redis/config.rb @@ -0,0 +1,48 @@ +module Redis::Config + DEFAULT_SENTINEL_PORT = '26379'.freeze + SIDEKIQ_SIZE = 25 + + class << self + def app + return BASE_CONFIG if const_defined? 'BASE_CONFIG' + + reload_config + end + + def sidekiq + config = begin + if const_defined? 'BASE_CONFIG' + BASE_CONFIG + else + reload_config + end + end + config.merge(size: SIDEKIQ_SIZE) + end + + def reload_config + config = { + url: ENV.fetch('REDIS_URL', 'redis://127.0.0.1:6379'), + password: ENV.fetch('REDIS_PASSWORD', nil).presence + } + + sentinel_string = ENV.fetch('REDIS_SENTINELS', nil) + if sentinel_string.presence + # expected format for REDIS_SENTINELS url string is host1:port1, host2:port2 + sentinels = sentinel_string.split(',').map do |sentinel_url| + host, port = sentinel_url.split(':').map(&:strip) + { host: host, port: port || DEFAULT_SENTINEL_PORT, password: config[:password] } + end + + master_name = ENV.fetch('REDIS_SENTINEL_MASTER_NAME', 'mymaster') + # over-write redis url as redis://:@/ when using sentinel + # more at https://github.com/redis/redis-rb/issues/531#issuecomment-263501322 + config[:url] = "redis://#{master_name}" + config[:sentinels] = sentinels + end + send(:remove_const, 'BASE_CONFIG') if const_defined? 'BASE_CONFIG' + const_set('BASE_CONFIG', config) + BASE_CONFIG + end + end +end diff --git a/spec/lib/redis/config_spec.rb b/spec/lib/redis/config_spec.rb new file mode 100644 index 000000000..d586c1bc3 --- /dev/null +++ b/spec/lib/redis/config_spec.rb @@ -0,0 +1,65 @@ +require 'rails_helper' + +describe ::Redis::Config do + context 'when single redis instance is used' do + let(:redis_url) { 'redis://my-redis-instance:6379' } + let(:redis_pasword) { 'some-strong-password' } + + before do + allow(ENV).to receive(:fetch).with('REDIS_URL', 'redis://127.0.0.1:6379').and_return(redis_url) + allow(ENV).to receive(:fetch).with('REDIS_PASSWORD', nil).and_return(redis_pasword) + allow(ENV).to receive(:fetch).with('REDIS_SENTINELS', nil).and_return('') + allow(ENV).to receive(:fetch).with('REDIS_SENTINEL_MASTER_NAME', 'mymaster').and_return('') + described_class.reload_config + end + + it 'checks for app redis config' do + app_config = described_class.app + expect(app_config.keys).to match_array([:url, :password]) + expect(app_config[:url]).to eq(redis_url) + expect(app_config[:password]).to eq(redis_pasword) + end + + it 'checks for sidekiq redis config' do + sidekiq_config = described_class.sidekiq + expect(sidekiq_config.keys).to match_array([:url, :password, :size]) + expect(sidekiq_config[:url]).to eq redis_url + expect(sidekiq_config[:password]).to eq redis_pasword + expect(sidekiq_config[:size]).to eq described_class::SIDEKIQ_SIZE + end + end + + context 'when redis sentinel is used' do + let(:redis_url) { 'redis://my-redis-instance:6379' } + let(:redis_sentinels) { 'sentinel_1:1234, sentinel_2:4321, sentinel_3' } + let(:redis_master_name) { 'master-name' } + let(:redis_pasword) { 'some-strong-password' } + + let(:expected_sentinels) do + [ + { host: 'sentinel_1', port: '1234', password: 'some-strong-password' }, + { host: 'sentinel_2', port: '4321', password: 'some-strong-password' }, + { host: 'sentinel_3', port: '26379', password: 'some-strong-password' } + ] + end + + before do + allow(ENV).to receive(:fetch).with('REDIS_URL', 'redis://127.0.0.1:6379').and_return(redis_url) + allow(ENV).to receive(:fetch).with('REDIS_PASSWORD', nil).and_return(redis_pasword) + allow(ENV).to receive(:fetch).with('REDIS_SENTINELS', nil).and_return(redis_sentinels) + allow(ENV).to receive(:fetch).with('REDIS_SENTINEL_MASTER_NAME', 'mymaster').and_return(redis_master_name) + described_class.reload_config + end + + it 'checks for app redis config' do + expect(described_class.app.keys).to match_array([:url, :password, :sentinels]) + expect(described_class.app[:url]).to eq("redis://#{redis_master_name}") + expect(described_class.app[:sentinels]).to match_array(expected_sentinels) + end + + it 'checks for sidekiq redis config' do + expect(described_class.sidekiq.keys).to match_array([:url, :password, :sentinels, :size]) + expect(described_class.sidekiq[:size]).to eq described_class::SIDEKIQ_SIZE + end + end +end