From 630200c32e7eea4756e9c9c17d1fdd90371b04ad Mon Sep 17 00:00:00 2001 From: Brian Palmer Date: Wed, 6 Jul 2011 15:43:19 -0600 Subject: [PATCH] support redis as well as memcache for a rails cache store closes #4498 Change-Id: Icf29882d8c0d351574496ba0494c1d8c518a3e7f Reviewed-on: https://gerrit.instructure.com/4580 Tested-by: Hudson Reviewed-by: Zach Wily --- Gemfile | 2 +- config/cache_store.yml.example | 13 +++++++++ config/environment.rb | 7 ----- config/environments/development.rb | 6 +++++ config/environments/production.rb | 6 +++++ config/environments/test.rb | 2 ++ config/memcache.yml.example | 3 +-- lib/canvas.rb | 26 ++++++++++++++++++ lib/canvas/redis.rb | 43 ++++++++++++++++++++++++++++++ 9 files changed, 98 insertions(+), 10 deletions(-) create mode 100644 config/cache_store.yml.example create mode 100644 lib/canvas/redis.rb diff --git a/Gemfile b/Gemfile index 7f0497c5c50..0c752be0f64 100644 --- a/Gemfile +++ b/Gemfile @@ -75,7 +75,7 @@ group :development do end group :redis do - gem 'redis-store', '1.0.0.beta4' + gem 'redis-store', '1.0.0.rc1' end # The closure-compiler gem has an undocumented diff --git a/config/cache_store.yml.example b/config/cache_store.yml.example new file mode 100644 index 00000000000..c4ce2f626e6 --- /dev/null +++ b/config/cache_store.yml.example @@ -0,0 +1,13 @@ +# if this file doesn't exist, memcache will be used if there are any +# servers configured in config/memcache.yml +development: + cache_store: mem_cache_store + # if no servers are specified, we'll look in config/memcache.yml + # servers: + # - localhost + # + # cache_store: redis_store + # # if no servers are specified, we'll look in config/redis.yml + # servers: + # - localhost + # database: 0 diff --git a/config/environment.rb b/config/environment.rb index 1cf7980fe58..8a8b32bf1b8 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -35,13 +35,6 @@ Rails::Initializer.run do |config| # Run "rake -D time" for a list of tasks for finding time zone names. Comment line to use default local time. config.time_zone = 'UTC' - memcache_servers = File.exists?(Rails.root+"config/memcache.yml") ? (YAML.load_file(Rails.root+"config/memcache.yml")[RAILS_ENV] || []) : [] - if memcache_servers.empty? - config.cache_store = :nil_store - else - config.cache_store = :mem_cache_store, *memcache_servers - end - log_config = File.exists?(Rails.root+"config/logging.yml") && YAML.load_file(Rails.root+"config/logging.yml")[RAILS_ENV] if log_config && log_config["logger"] == "syslog" require 'syslog_wrapper' diff --git a/config/environments/development.rb b/config/environments/development.rb index 6ab6f5c7cb6..0ccc5bd0223 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -30,5 +30,11 @@ end # eval -local.rb if it exists Dir[File.dirname(__FILE__) + "/" + File.basename(__FILE__, ".rb") + "-*.rb"].each { |localfile| eval(File.new(localfile).read) } +# initialize cache store +# this needs to happen in each environment config file, rather than a +# config/initializer/* file, to allow Rails' full initialization of the cache +# to take place, including middleware inserts and such. +config.cache_store = Canvas.cache_store_config + # allow debugging only in development environment by default require "ruby-debug" diff --git a/config/environments/production.rb b/config/environments/production.rb index 71ea135faf9..1cb4df91862 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -17,3 +17,9 @@ config.action_view.cache_template_loading = true # eval -local.rb if it exists Dir[File.dirname(__FILE__) + "/" + File.basename(__FILE__, ".rb") + "-*.rb"].each { |localfile| eval(File.new(localfile).read) } + +# initialize cache store +# this needs to happen in each environment config file, rather than a +# config/initializer/* file, to allow Rails' full initialization of the cache +# to take place, including middleware inserts and such. +config.cache_store = Canvas.cache_store_config diff --git a/config/environments/test.rb b/config/environments/test.rb index c49e6049de0..4517e92acc2 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -32,3 +32,5 @@ Canvas.dynamic_finder_type_cast_error = :raise # eval -local.rb if it exists Dir[File.dirname(__FILE__) + "/" + File.basename(__FILE__, ".rb") + "-*.rb"].each { |localfile| eval(File.new(localfile).read) } + +config.cache_store = :nil_store diff --git a/config/memcache.yml.example b/config/memcache.yml.example index 15fd8f0869b..e632619afe8 100644 --- a/config/memcache.yml.example +++ b/config/memcache.yml.example @@ -1,5 +1,4 @@ -# if an environment has no servers defined, we'll use NilStore instead, which -# effectively disables caching +# see also config/cache_store.yml.example production: development: diff --git a/lib/canvas.rb b/lib/canvas.rb index fd63b496752..7b63b04538c 100644 --- a/lib/canvas.rb +++ b/lib/canvas.rb @@ -47,6 +47,32 @@ module Canvas @redis_enabled ||= Setting.from_config('redis').present? end + def self.cache_store_config + cache_store_config = { + 'cache_store' => 'mem_cache_store', + }.merge(Setting.from_config('cache_store') || {}) + config = nil + case cache_store_config.delete('cache_store') + when 'mem_cache_store' + cache_store_config['namespace'] ||= cache_store_config['key'] + servers = cache_store_config['servers'] || (Setting.from_config('memcache')) + if servers + config = :mem_cache_store, servers, cache_store_config + end + when 'redis_store' + Bundler.require 'redis' + Canvas::Redis.patch + # merge in redis.yml, but give precedence to cache_store.yml + cache_store_config = (Setting.from_config('redis') || {}).merge(cache_store_config) + cache_store_config['key_prefix'] ||= cache_store_config['key'] + config = :redis_store, cache_store_config + end + unless config + config = :nil_store + end + config + end + # `sample` reports KB, not B if File.directory?("/proc") # linux w/ proc fs diff --git a/lib/canvas/redis.rb b/lib/canvas/redis.rb new file mode 100644 index 00000000000..04d862879f8 --- /dev/null +++ b/lib/canvas/redis.rb @@ -0,0 +1,43 @@ +module Canvas::Redis + # while we wait for this pull request + # https://github.com/jodosha/redis-store/pull/83 + def self.patch + + ::ActiveSupport::Cache::RedisStore.class_eval do + def write_with_econnrefused(key, value, options = nil) + write_without_econnrefused(key, value, options) + rescue Errno::ECONNREFUSED => e + false + end + alias_method_chain :write, :econnrefused + + def read_with_econnrefused(key, options = nil) + read_without_econnrefused(key, options) + rescue Errno::ECONNREFUSED => e + nil + end + alias_method_chain :read, :econnrefused + + def delete_with_econnrefused(key, options = nil) + delete_without_econnrefused(key, options) + rescue Errno::ECONNREFUSED => e + false + end + alias_method_chain :delete, :econnrefused + + def exist_with_econnrefused?(key, options = nil) + exist_without_econnrefused?(key, options = nil) + rescue Errno::ECONNREFUSED => e + false + end + alias_method_chain :exist?, :econnrefused + + def delete_matched_with_econnrefused(matcher, options = nil) + delete_matched_without_econnrefused(matcher, options) + rescue Errno::ECONNREFUSED => e + false + end + alias_method_chain :delete_matched, :econnrefused + end + end +end