ngmoco-cache-money 0.2.23 → 0.2.24.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,152 @@
1
+ # Maps Redis methods and semantics to those of memcache-client
2
+ module Cash
3
+ module Adapter
4
+ class Redis
5
+ def initialize(repository, options = {})
6
+ @repository = repository
7
+ @logger = options[:logger]
8
+ @default_ttl = options[:default_ttl] || raise(":default_ttl is a required option")
9
+ end
10
+
11
+ def add(key, value, ttl=nil, raw = false)
12
+ wrap(key, not_stored) do
13
+ logger.debug("Redis add: #{key.inspect}") if debug_logger?
14
+ value = dump(value) unless raw
15
+ # TODO: make transactional
16
+ result = @repository.setnx(key, value)
17
+ @repository.expires(key, ttl || @default_ttl) if 1 == result
18
+ logger.debug("Redis hit: #{key.inspect}") if debug_logger?
19
+ 1 == result ? stored : not_stored
20
+ end
21
+ end
22
+
23
+ def get(key, raw = false)
24
+ wrap(key) do
25
+ logger.debug("Redis get: #{key.inspect}") if debug_logger?
26
+ value = wrap(key) { @repository.get(key) }
27
+ if value
28
+ logger.debug("Redis hit: #{key.inspect}") if debug_logger?
29
+ value = load(value) unless raw
30
+ else
31
+ logger.debug("Redis miss: #{key.inspect}") if debug_logger?
32
+ end
33
+ value
34
+ end
35
+ end
36
+
37
+ def get_multi(*keys)
38
+ wrap(keys, {}) do
39
+ keys.flatten!
40
+ logger.debug("Redis get_multi: #{keys.inspect}") if debug_logger?
41
+
42
+ # Values are returned as an array. Convert them to a hash of matches, dropping anything
43
+ # that doesn't have a match.
44
+ values = @repository.mget(*keys)
45
+ result = {}
46
+ keys.each_with_index{ |key, i| result[key] = load(values[i]) if values[i] }
47
+
48
+ if result.any?
49
+ logger.debug("Redis hit: #{keys.inspect}") if debug_logger?
50
+ else
51
+ logger.debug("Redis miss: #{keys.inspect}") if debug_logger?
52
+ end
53
+ result
54
+ end
55
+ end
56
+
57
+ def set(key, value, ttl=nil, raw = false)
58
+ wrap(key, not_stored) do
59
+ logger.debug("Redis set: #{key.inspect}") if debug_logger?
60
+ value = dump(value) unless raw
61
+ @repository.setex(key, ttl || @default_ttl, value)
62
+ logger.debug("Redis hit: #{key.inspect}") if debug_logger?
63
+ stored
64
+ end
65
+ end
66
+
67
+ def delete(key)
68
+ wrap(key, not_found) do
69
+ logger.debug("Redis delete: #{key.inspect}") if debug_logger?
70
+ @repository.del(key)
71
+ logger.debug("Redis hit: #{key.inspect}") if debug_logger?
72
+ deleted
73
+ end
74
+ end
75
+
76
+ def get_server_for_key(key)
77
+ wrap(key) do
78
+ # Redis::Distributed has a node_for method.
79
+ client = @repository.respond_to?(:node_for) ? @repository.node_for(key) : @repository.client
80
+ client.id
81
+ end
82
+ end
83
+
84
+ def incr(key, value = 1)
85
+ # Redis always answeres positively to incr/decr but memcache does not and waits for the key
86
+ # to be added in a separate operation.
87
+ if wrap(nil) { @repository.exists(key) }
88
+ wrap(key) { @repository.incrby(key, value).to_i }
89
+ end
90
+ end
91
+
92
+ def decr(key, value = 1)
93
+ if wrap(nil) { @repository.exists(key) }
94
+ wrap(key) { @repository.decrby(key, value).to_i }
95
+ end
96
+ end
97
+
98
+ def flush_all
99
+ @repository.flushall
100
+ end
101
+
102
+ def exception_classes
103
+ [Errno::ECONNRESET, Errno::EPIPE, Errno::ECONNABORTED, Errno::EBADF, Errno::EINVAL]
104
+ end
105
+
106
+ private
107
+
108
+ def logger
109
+ @logger
110
+ end
111
+
112
+ def debug_logger?
113
+ logger && logger.respond_to?(:debug?) && logger.debug?
114
+ end
115
+
116
+ def wrap(key, error_value = nil)
117
+ yield
118
+ rescue *exception_classes
119
+ log_error($!) if logger
120
+ error_value
121
+ end
122
+
123
+ def dump(value)
124
+ Marshal.dump(value)
125
+ end
126
+
127
+ def load(value)
128
+ Marshal.load(value)
129
+ end
130
+
131
+ def stored
132
+ "STORED\r\n"
133
+ end
134
+
135
+ def deleted
136
+ "DELETED\r\n"
137
+ end
138
+
139
+ def not_stored
140
+ "NOT_STORED\r\n"
141
+ end
142
+
143
+ def not_found
144
+ "NOT_FOUND\r\n"
145
+ end
146
+
147
+ def log_error(err)
148
+ logger.error("Redis ERROR, #{err.class}: #{err}") if logger
149
+ end
150
+ end
151
+ end
152
+ end
@@ -16,7 +16,7 @@ module Cash
16
16
  def self.extended(a_class)
17
17
  class << a_class
18
18
  def cache_config
19
- @cache_config ? @cache_config : superclass.cache_config
19
+ @cache_config
20
20
  end
21
21
 
22
22
  delegate :repository, :indices, :to => :cache_config
@@ -26,7 +26,7 @@ module Cash
26
26
 
27
27
  def inherited_with_cache_config(subclass)
28
28
  inherited_without_cache_config(subclass)
29
- @cache_config.inherit(subclass)
29
+ @cache_config.inherit(subclass) if @cache_config
30
30
  end
31
31
 
32
32
  def index(attributes, options = {})
@@ -41,6 +41,10 @@ module Cash
41
41
  def cache_config=(config)
42
42
  @cache_config = config
43
43
  end
44
+
45
+ def cacheable?(*args)
46
+ Cash.enabled && cache_config
47
+ end
44
48
  end
45
49
 
46
50
  class Config
@@ -55,8 +59,7 @@ module Cash
55
59
  end
56
60
 
57
61
  def ttl
58
- repository_ttl = repository.respond_to?(:default_ttl) ? repository.default_ttl : nil
59
- @ttl ||= @options[:ttl] || repository_ttl || 1.day
62
+ @ttl ||= (repository.respond_to?(:default_ttl) && repository.default_ttl) || @options[:ttl]
60
63
  end
61
64
 
62
65
  def version
@@ -21,17 +21,29 @@ module Cash
21
21
 
22
22
  # User.find(:first, ...), User.find_by_foo(...), User.find(:all, ...), User.find_all_by_foo(...)
23
23
  def find_every_with_cache(options)
24
- Query::Select.perform(self, options, scope(:find))
24
+ if cacheable?
25
+ Query::Select.perform(self, options, scope(:find))
26
+ else
27
+ find_every_without_cache(options)
28
+ end
25
29
  end
26
30
 
27
31
  # User.find(1), User.find(1, 2, 3), User.find([1, 2, 3]), User.find([])
28
32
  def find_from_ids_with_cache(ids, options)
29
- Query::PrimaryKey.perform(self, ids, options, scope(:find))
33
+ if cacheable?
34
+ Query::PrimaryKey.perform(self, ids, options, scope(:find))
35
+ else
36
+ find_from_ids_without_cache(ids, options)
37
+ end
30
38
  end
31
39
 
32
40
  # User.count(:all), User.count, User.sum(...)
33
41
  def calculate_with_cache(operation, column_name, options = {})
34
- Query::Calculation.perform(self, operation, column_name, options, scope(:find))
42
+ if cacheable?
43
+ Query::Calculation.perform(self, operation, column_name, options, scope(:find))
44
+ else
45
+ calculate_without_cache(operation, column_name, options)
46
+ end
35
47
  end
36
48
  end
37
49
  end
@@ -4,11 +4,8 @@ module Cash
4
4
  delegate :each, :hash, :to => :@attributes
5
5
  delegate :get, :set, :expire, :find_every_without_cache, :calculate_without_cache, :calculate_with_cache, :incr, :decr, :primary_key, :logger, :to => :@active_record
6
6
 
7
- DEFAULT_OPTIONS = { :ttl => 1.day }
8
-
9
7
  def initialize(config, active_record, attributes, options = {})
10
- DEFAULT_OPTIONS[:ttl] = config.ttl || DEFAULT_OPTIONS[:ttl]
11
- @config, @active_record, @attributes, @options = config, active_record, Array(attributes).collect(&:to_s).sort, DEFAULT_OPTIONS.merge(options)
8
+ @config, @active_record, @attributes, @options = config, active_record, Array(attributes).collect(&:to_s).sort, options
12
9
  end
13
10
 
14
11
  def ==(other)
@@ -47,7 +44,7 @@ module Cash
47
44
 
48
45
  module Attributes
49
46
  def ttl
50
- @ttl ||= options[:ttl] || config.ttl
47
+ @ttl ||= options[:ttl] || @config.ttl
51
48
  end
52
49
 
53
50
  def order
@@ -129,7 +126,7 @@ module Cash
129
126
  (a <=> b) * (order == :asc ? 1 : -1)
130
127
  end.uniq
131
128
  objects = truncate_if_necessary(objects)
132
- set(key, objects, :ttl => ttl)
129
+ set(key, objects, :ttl => ttl)
133
130
  incr("#{key}/count") { calculate_at_index(:count, attribute_value_pairs) }
134
131
  end
135
132
  end
@@ -15,7 +15,7 @@ module Cash
15
15
 
16
16
  def autoload_missing_constants
17
17
  yield if block_given?
18
- rescue ArgumentError, MemCache::MemCacheError => error
18
+ rescue ArgumentError, *@remote_cache.exception_classes => error
19
19
  lazy_load ||= Hash.new { |hash, hash_key| hash[hash_key] = true; false }
20
20
  if error.to_s[/undefined class|referred/] && !lazy_load[error.to_s.split.last.constantize]
21
21
  retry
@@ -33,7 +33,7 @@ module Cash
33
33
  exponential_sleep(count, initial_wait) unless count == retries - 1
34
34
  end
35
35
  debug_lock(key)
36
- raise Error, "Couldn't acquire memcache lock on #{@cache.get_server_for_key("lock/#{key}")}"
36
+ raise Error, "Couldn't acquire memcache lock on 'lock/#{key}'"
37
37
  end
38
38
 
39
39
  def release_lock(key)
@@ -41,7 +41,7 @@ module Cash
41
41
  end
42
42
 
43
43
  def exponential_sleep(count, initial_wait)
44
- sleep((2**count) / initial_wait)
44
+ sleep((2**count) * initial_wait)
45
45
  end
46
46
 
47
47
  private
@@ -20,7 +20,7 @@ module Cash
20
20
  @value = Marshal.dump(value)
21
21
  end
22
22
 
23
- if ttl.zero?
23
+ if ttl.nil? || ttl.zero?
24
24
  @ttl = self.class.default_ttl
25
25
  else
26
26
  @ttl = ttl
@@ -68,18 +68,22 @@ module Cash
68
68
  end
69
69
 
70
70
  def get(key, raw = false)
71
- log "< get #{key}"
72
- unless self.has_unexpired_key?(key)
73
- log('> END')
74
- return nil
75
- end
76
-
77
- log("> sending key #{key}")
78
- log('> END')
79
- if raw
80
- self[key].value
71
+ if key.is_a?(Array)
72
+ get_multi(*key)
81
73
  else
82
- self[key].unmarshal
74
+ log "< get #{key}"
75
+ unless self.has_unexpired_key?(key)
76
+ log('> END')
77
+ return nil
78
+ end
79
+
80
+ log("> sending key #{key}")
81
+ log('> END')
82
+ if raw
83
+ self[key].value
84
+ else
85
+ self[key].unmarshal
86
+ end
83
87
  end
84
88
  end
85
89
 
@@ -61,8 +61,22 @@ module Cash
61
61
 
62
62
  private
63
63
  def cacheable?(*optionss)
64
- return false if @active_record.respond_to?(:cacheable?) && ! @active_record.cacheable?(*optionss)
65
- optionss.each { |options| return unless safe_options_for_cache?(options) }
64
+ if @active_record.respond_to?(:cacheable?) && ! @active_record.cacheable?(*optionss)
65
+ if logger
66
+ if @active_record.respond_to?(:cacheable?)
67
+ logger.debug(" \e[1;4;31mUNCACHEABLE CLASS\e[0m #{table_name}")
68
+ else
69
+ logger.debug(" \e[1;4;31mUNCACHEABLE INSTANCE\e[0m #{table_name} - #{optionss.inspect}")
70
+ end
71
+ end
72
+ return false
73
+ end
74
+ optionss.each do |options|
75
+ unless safe_options_for_cache?(options)
76
+ logger.debug(" \e[1;4;31mUNCACHEABLE UNSAFE\e[0m #{table_name} - #{options.inspect}") if logger
77
+ return false
78
+ end
79
+ end
66
80
  partial_indices = optionss.collect { |options| attribute_value_pairs_for_conditions(options[:conditions]) }
67
81
  return if partial_indices.include?(nil)
68
82
  attribute_value_pairs = partial_indices.sum.sort { |x, y| x[0] <=> y[0] }
@@ -74,7 +88,13 @@ module Cash
74
88
  if index = indexed_on?(attribute_value_pairs.collect { |pair| pair[0] })
75
89
  if index.matches?(self)
76
90
  [attribute_value_pairs, index]
91
+ else
92
+ logger.debug(" \e[1;4;31mUNCACHEABLE NO MATCHING INDEX\e[0m #{table_name} - #{index.order_column.inspect} #{index.order.inspect} #{index.limit.inspect}") if logger
93
+ false
77
94
  end
95
+ else
96
+ logger.debug(" \e[1;4;31mUNCACHEABLE NOT INDEXED\e[0m #{table_name} - #{attribute_value_pairs.collect { |pair| pair[0] }.inspect}") if logger
97
+ false
78
98
  end
79
99
  end
80
100
 
@@ -191,7 +211,13 @@ module Cash
191
211
  order_sql = @options1[:order] || @options2[:order]
192
212
  options[:order] = order_sql if order_sql
193
213
  results = find_from_ids_without_cache(missing_ids, options)
194
- results.each {|o| @active_record.add_to_caches(o) } if results && results.is_a?(Array)
214
+ if results
215
+ if results.is_a?(Array)
216
+ results.each {|o| @active_record.add_to_caches(o) }
217
+ else
218
+ @active_record.add_to_caches(results)
219
+ end
220
+ end
195
221
  results
196
222
  end
197
223
  end
@@ -0,0 +1,3 @@
1
+ module Cash
2
+ VERSION = '0.2.24.2'
3
+ end
@@ -1,6 +1,6 @@
1
1
  module Cash
2
2
  module WriteThrough
3
- DEFAULT_TTL = 12.hours
3
+ DEFAULT_TTL = 12.hours.to_i
4
4
 
5
5
  def self.included(active_record_class)
6
6
  active_record_class.class_eval do
@@ -50,19 +50,23 @@ module Cash
50
50
 
51
51
  module ClassMethods
52
52
  def add_to_caches(object)
53
- indices.each { |index| index.add(object) } if cache_config
53
+ indices.each { |index| index.add(object) } if cacheable?
54
+ true
54
55
  end
55
56
 
56
57
  def update_caches(object)
57
- indices.each { |index| index.update(object) } if cache_config
58
+ indices.each { |index| index.update(object) } if cacheable?
59
+ true
58
60
  end
59
61
 
60
62
  def remove_from_caches(object)
61
- indices.each { |index| index.remove(object) } if cache_config
63
+ indices.each { |index| index.remove(object) } if cacheable?
64
+ true
62
65
  end
63
66
 
64
67
  def expire_caches(object)
65
- indices.each { |index| index.delete(object) } if cache_config
68
+ indices.each { |index| index.delete(object) } if cacheable?
69
+ true
66
70
  end
67
71
  end
68
72
  end
@@ -126,7 +126,7 @@ require 'memcached'
126
126
 
127
127
  private
128
128
  def expires_in(options)
129
- (options && options[:expires_in]) || 0
129
+ (options && options[:expires_in] && options[:expires_in].to_i) || 0
130
130
  end
131
131
 
132
132
  def marshal?(options)
@@ -1,40 +1 @@
1
- yml = YAML.load(IO.read(File.join(RAILS_ROOT, "config", "memcached.yml")))
2
- memcache_config = yml[RAILS_ENV]
3
- memcache_config.symbolize_keys! if memcache_config.respond_to?(:symbolize_keys!)
4
-
5
- if defined?(DISABLE_CACHE_MONEY) || ENV['DISABLE_CACHE_MONEY'] == 'true' || memcache_config.nil? || memcache_config[:cache_money] != true
6
- Rails.logger.info 'cache-money disabled'
7
- class ActiveRecord::Base
8
- def self.index(*args)
9
- end
10
- end
11
- else
12
- Rails.logger.info 'cache-money enabled'
13
- require 'cache_money'
14
-
15
- memcache_config[:logger] = Rails.logger
16
- memcache_servers =
17
- case memcache_config[:servers].class.to_s
18
- when "String"; memcache_config[:servers].gsub(' ', '').split(',')
19
- when "Array"; memcache_config[:servers]
20
- end
21
- $memcache = MemcachedWrapper.new(memcache_servers, memcache_config)
22
-
23
- #ActionController::Base.cache_store = :cache_money_mem_cache_store
24
- ActionController::Base.session_options[:cache] = $memcache if memcache_config[:sessions]
25
- #silence_warnings {
26
- # Object.const_set "RAILS_CACHE", ActiveSupport::Cache.lookup_store(:cache_money_mem_cache_store)
27
- #}
28
-
29
- $local = Cash::Local.new($memcache)
30
- $lock = Cash::Lock.new($memcache)
31
- $cache = Cash::Transactional.new($local, $lock)
32
-
33
- # allow setting up caching on a per-model basis
34
- unless memcache_config[:automatic_caching].to_s == 'false'
35
- Rails.logger.info "cache-money: global model caching enabled"
36
- class ActiveRecord::Base
37
- is_cached(:repository => $cache)
38
- end
39
- end
40
- end
1
+ require 'cache_money'