Class: CachedModel
- Inherits:
-
ActiveRecord::Base
- Object
- ActiveRecord::Base
- CachedModel
- Defined in:
- lib/cached_model.rb
Overview
An abstract ActiveRecord descendant that caches records in memcache and in local memory.
CachedModel can store into both a local in-memory cache and in memcached. By default memcached is enabled and the local cache is disabled.
Local cache use can be enabled or disabled with CachedModel::use_local_cache=. If you do enable the local cache be sure to add a before filter that calls CachedModel::cache_reset for every request.
memcached use can be enabled or disabled with CachedModel::use_memcache=.
You can adjust the memcached TTL with CachedModel::ttl=
Class Attribute Summary collapse
-
.cache_delay_commit ⇒ Object
The transaction commit buffer.
-
.cache_local ⇒ Object
readonly
The local process cache.
-
.cache_transaction_level ⇒ Object
The transaction nesting level.
-
.ttl ⇒ Object
Memcache record time-to-live for stored records.
-
.use_local_cache ⇒ Object
writeonly
Enables or disables use of the local cache.
-
.use_memcache ⇒ Object
writeonly
Enables or disables the use of memcache.
Class Method Summary collapse
-
.cache_delete(klass, id) ⇒ Object
Invalidate the cache entry for a record.
-
.cache_reset ⇒ Object
Invalidate the local process cache.
-
.find(*args) ⇒ Object
Override the find method to look for values in the cache before going to the database.
-
.find_by_sql(*args) ⇒ Object
Find by primary key from the cache.
-
.transaction(*args) ⇒ Object
Delay updating the cache while in a transaction.
-
.use_local_cache? ⇒ Boolean
Returns true if use of the local cache is enabled.
-
.use_memcache? ⇒ Boolean
Returns true if use of memcache is enabled.
Instance Method Summary collapse
-
#cache_delete ⇒ Object
Remove this record from the cache.
-
#cache_key_local ⇒ Object
The local cache key for this record.
-
#cache_key_memcache ⇒ Object
The memcache key for this record.
-
#cache_local ⇒ Object
The local object cache.
-
#cache_store ⇒ Object
Store this record in the cache without associations.
-
#destroy ⇒ Object
Delete the entry from the cache now that it isn’t in the DB.
-
#reload ⇒ Object
Invalidate the cache for this record before reloading from the DB.
-
#update ⇒ Object
Store a new copy of ourselves into the cache.
Class Attribute Details
.cache_delay_commit ⇒ Object
The transaction commit buffer. You shouldn’t touch me.
37 38 39 |
# File 'lib/cached_model.rb', line 37 def cache_delay_commit @cache_delay_commit end |
.cache_local ⇒ Object (readonly)
The local process cache. You shouldn’t touch me.
42 43 44 |
# File 'lib/cached_model.rb', line 42 def cache_local @cache_local end |
.cache_transaction_level ⇒ Object
The transaction nesting level. You shouldn’t touch me.
47 48 49 |
# File 'lib/cached_model.rb', line 47 def cache_transaction_level @cache_transaction_level end |
.ttl ⇒ Object
Memcache record time-to-live for stored records.
69 70 71 |
# File 'lib/cached_model.rb', line 69 def ttl @ttl end |
.use_local_cache=(value) ⇒ Object (writeonly)
Enables or disables use of the local cache.
NOTE if you enable this you must call #cache_reset or you will experience uncontrollable process growth!
Defaults to false.
59 60 61 |
# File 'lib/cached_model.rb', line 59 def use_local_cache=(value) @use_local_cache = value end |
.use_memcache=(value) ⇒ Object (writeonly)
Enables or disables the use of memcache.
64 65 66 |
# File 'lib/cached_model.rb', line 64 def use_memcache=(value) @use_memcache = value end |
Class Method Details
.cache_delete(klass, id) ⇒ Object
Invalidate the cache entry for a record. The update method will automatically invalidate the cache when updates are made through ActiveRecord model record. However, several methods update tables with direct sql queries for effeciency. These methods should call this method to invalidate the cache after making those changes.
NOTE - if a SQL query updates multiple rows with one query, there is currently no way to invalidate the affected entries unless the entire cache is dumped or until the TTL expires, so try not to do this.
102 103 104 105 106 |
# File 'lib/cached_model.rb', line 102 def self.cache_delete(klass, id) key = "#{klass}:#{id}" CachedModel.cache_local.delete key if CachedModel.use_local_cache? Cache.delete "active_record:#{key}" if CachedModel.use_memcache? end |
.cache_reset ⇒ Object
Invalidate the local process cache. This should be called from a before filter at the beginning of each request.
112 113 114 |
# File 'lib/cached_model.rb', line 112 def self.cache_reset CachedModel.cache_local.clear if CachedModel.use_local_cache? end |
.find(*args) ⇒ Object
Override the find method to look for values in the cache before going to the database. – TODO Push a bunch of code down into find_by_sql where it really should belong.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# File 'lib/cached_model.rb', line 123 def self.find(*args) args[0] = args.first.to_i if args.first =~ /\A\d+\Z/ # Only handle simple find requests. If the request was more complicated, # let the base class handle it, but store the retrieved records in the # local cache in case we need them later. if args.length != 1 or not Fixnum === args.first then # Rails requires multiple levels of indirection to look up a record # First call super records = super # Then, if it was a :all, just return return records if args.first == :all return records if RAILS_ENV == 'test' case records when Array then records.each { |r| r.cache_store } end return records end return super end |
.find_by_sql(*args) ⇒ Object
Find by primary key from the cache.
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/cached_model.rb', line 148 def self.find_by_sql(*args) return super unless args.first =~ /^SELECT \* FROM #{table_name} WHERE \(#{table_name}\.#{primary_key} = '?(\d+)'?\) +LIMIT 1/ id = $1.to_i # Try to find the record in the local cache. cache_key_local = "#{name}:#{id}" if CachedModel.use_local_cache? then record = CachedModel.cache_local[cache_key_local] return [record] unless record.nil? end # Try to find the record in memcache and add it to the local cache if CachedModel.use_memcache? then record = Cache.get "active_record:#{cache_key_local}" unless record.nil? then if CachedModel.use_local_cache? then CachedModel.cache_local[cache_key_local] = record end return [record] end end # Fetch the record from the DB records = super records.first.cache_store unless records.empty? # only one return records end |
.transaction(*args) ⇒ Object
Delay updating the cache while in a transaction.
180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/cached_model.rb', line 180 def self.transaction(*args) level = CachedModel.cache_transaction_level += 1 CachedModel.cache_delay_commit[level] = [] value = super waiting = CachedModel.cache_delay_commit.delete level waiting.each do |obj| obj.cache_store end return value ensure CachedModel.cache_transaction_level -= 1 end |
.use_local_cache? ⇒ Boolean
Returns true if use of the local cache is enabled.
197 198 199 |
# File 'lib/cached_model.rb', line 197 def self.use_local_cache? return @use_local_cache end |
.use_memcache? ⇒ Boolean
Returns true if use of memcache is enabled.
204 205 206 |
# File 'lib/cached_model.rb', line 204 def self.use_memcache? return @use_memcache end |
Instance Method Details
#cache_delete ⇒ Object
Remove this record from the cache.
239 240 241 242 |
# File 'lib/cached_model.rb', line 239 def cache_delete cache_local.delete cache_key_local if CachedModel.use_local_cache? Cache.delete cache_key_memcache if CachedModel.use_memcache? end |
#cache_key_local ⇒ Object
The local cache key for this record.
247 248 249 |
# File 'lib/cached_model.rb', line 247 def cache_key_local return "#{self.class}:#{id}" end |
#cache_key_memcache ⇒ Object
The memcache key for this record.
254 255 256 |
# File 'lib/cached_model.rb', line 254 def cache_key_memcache return "active_record:#{cache_key_local}" end |
#cache_local ⇒ Object
The local object cache.
261 262 263 |
# File 'lib/cached_model.rb', line 261 def cache_local return CachedModel.cache_local end |
#cache_store ⇒ Object
Store this record in the cache without associations. Storing associations leads to wasted cache space and hard-to-debug problems.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 |
# File 'lib/cached_model.rb', line 269 def cache_store obj = dup obj.send :instance_variable_set, :@attributes, attributes_before_type_cast if CachedModel.cache_delay_commit[CachedModel.cache_transaction_level].nil? then if CachedModel.use_local_cache? then cache_local[cache_key_local] = obj end if CachedModel.use_memcache? then Cache.put cache_key_memcache, obj, CachedModel.ttl end else CachedModel.cache_delay_commit[CachedModel.cache_transaction_level] << obj end nil end |
#destroy ⇒ Object
Delete the entry from the cache now that it isn’t in the DB.
211 212 213 214 215 |
# File 'lib/cached_model.rb', line 211 def destroy return super ensure cache_delete end |
#reload ⇒ Object
Invalidate the cache for this record before reloading from the DB.
220 221 222 223 224 225 |
# File 'lib/cached_model.rb', line 220 def reload cache_delete return super ensure cache_store end |
#update ⇒ Object
Store a new copy of ourselves into the cache.
230 231 232 233 234 |
# File 'lib/cached_model.rb', line 230 def update return super ensure cache_store end |