PageRenderTime 40ms CodeModel.GetById 8ms RepoModel.GetById 0ms app.codeStats 1ms

/activesupport/lib/active_support/cache.rb

https://github.com/ghar/rails
Ruby | 630 lines | 336 code | 53 blank | 241 comment | 43 complexity | 4442097949f8b558c00f74c2eb0c2ed6 MD5 | raw file
  1. require 'benchmark'
  2. require 'zlib'
  3. require 'active_support/core_ext/array/extract_options'
  4. require 'active_support/core_ext/array/wrap'
  5. require 'active_support/core_ext/benchmark'
  6. require 'active_support/core_ext/exception'
  7. require 'active_support/core_ext/class/attribute_accessors'
  8. require 'active_support/core_ext/numeric/bytes'
  9. require 'active_support/core_ext/numeric/time'
  10. require 'active_support/core_ext/object/to_param'
  11. require 'active_support/core_ext/string/inflections'
  12. module ActiveSupport
  13. # See ActiveSupport::Cache::Store for documentation.
  14. module Cache
  15. autoload :FileStore, 'active_support/cache/file_store'
  16. autoload :MemoryStore, 'active_support/cache/memory_store'
  17. autoload :MemCacheStore, 'active_support/cache/mem_cache_store'
  18. autoload :SynchronizedMemoryStore, 'active_support/cache/synchronized_memory_store'
  19. autoload :CompressedMemCacheStore, 'active_support/cache/compressed_mem_cache_store'
  20. # These options mean something to all cache implementations. Individual cache
  21. # implementations may support additional options.
  22. UNIVERSAL_OPTIONS = [:namespace, :compress, :compress_threshold, :expires_in, :race_condition_ttl]
  23. module Strategy
  24. autoload :LocalCache, 'active_support/cache/strategy/local_cache'
  25. end
  26. # Creates a new CacheStore object according to the given options.
  27. #
  28. # If no arguments are passed to this method, then a new
  29. # ActiveSupport::Cache::MemoryStore object will be returned.
  30. #
  31. # If you pass a Symbol as the first argument, then a corresponding cache
  32. # store class under the ActiveSupport::Cache namespace will be created.
  33. # For example:
  34. #
  35. # ActiveSupport::Cache.lookup_store(:memory_store)
  36. # # => returns a new ActiveSupport::Cache::MemoryStore object
  37. #
  38. # ActiveSupport::Cache.lookup_store(:mem_cache_store)
  39. # # => returns a new ActiveSupport::Cache::MemCacheStore object
  40. #
  41. # Any additional arguments will be passed to the corresponding cache store
  42. # class's constructor:
  43. #
  44. # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache")
  45. # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache")
  46. #
  47. # If the first argument is not a Symbol, then it will simply be returned:
  48. #
  49. # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new)
  50. # # => returns MyOwnCacheStore.new
  51. def self.lookup_store(*store_option)
  52. store, *parameters = *Array.wrap(store_option).flatten
  53. case store
  54. when Symbol
  55. store_class_name = store.to_s.camelize
  56. store_class =
  57. begin
  58. require "active_support/cache/#{store}"
  59. rescue LoadError => e
  60. raise "Could not find cache store adapter for #{store} (#{e})"
  61. else
  62. ActiveSupport::Cache.const_get(store_class_name)
  63. end
  64. store_class.new(*parameters)
  65. when nil
  66. ActiveSupport::Cache::MemoryStore.new
  67. else
  68. store
  69. end
  70. end
  71. def self.expand_cache_key(key, namespace = nil)
  72. expanded_cache_key = namespace ? "#{namespace}/" : ""
  73. prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"]
  74. if prefix
  75. expanded_cache_key << "#{prefix}/"
  76. end
  77. expanded_cache_key <<
  78. if key.respond_to?(:cache_key)
  79. key.cache_key
  80. elsif key.is_a?(Array)
  81. if key.size > 1
  82. key.collect { |element| expand_cache_key(element) }.to_param
  83. else
  84. key.first.to_param
  85. end
  86. elsif key
  87. key.to_param
  88. end.to_s
  89. expanded_cache_key
  90. end
  91. # An abstract cache store class. There are multiple cache store
  92. # implementations, each having its own additional features. See the classes
  93. # under the ActiveSupport::Cache module, e.g.
  94. # ActiveSupport::Cache::MemCacheStore. MemCacheStore is currently the most
  95. # popular cache store for large production websites.
  96. #
  97. # Some implementations may not support all methods beyond the basic cache
  98. # methods of +fetch+, +write+, +read+, +exist?+, and +delete+.
  99. #
  100. # ActiveSupport::Cache::Store can store any serializable Ruby object.
  101. #
  102. # cache = ActiveSupport::Cache::MemoryStore.new
  103. #
  104. # cache.read("city") # => nil
  105. # cache.write("city", "Duckburgh")
  106. # cache.read("city") # => "Duckburgh"
  107. #
  108. # Keys are always translated into Strings and are case sensitive. When an
  109. # object is specified as a key and has a +cache_key+ method defined, this
  110. # method will be called to define the key. Otherwise, the +to_param+
  111. # method will be called. Hashes and Arrays can also be used as keys. The
  112. # elements will be delimited by slashes, and the elements within a Hash
  113. # will be sorted by key so they are consistent.
  114. #
  115. # cache.read("city") == cache.read(:city) # => true
  116. #
  117. # Nil values can be cached.
  118. #
  119. # If your cache is on a shared infrastructure, you can define a namespace
  120. # for your cache entries. If a namespace is defined, it will be prefixed on
  121. # to every key. The namespace can be either a static value or a Proc. If it
  122. # is a Proc, it will be invoked when each key is evaluated so that you can
  123. # use application logic to invalidate keys.
  124. #
  125. # cache.namespace = lambda { @last_mod_time } # Set the namespace to a variable
  126. # @last_mod_time = Time.now # Invalidate the entire cache by changing namespace
  127. #
  128. #
  129. # Caches can also store values in a compressed format to save space and
  130. # reduce time spent sending data. Since there is overhead, values must be
  131. # large enough to warrant compression. To turn on compression either pass
  132. # <tt>:compress => true</tt> in the initializer or as an option to +fetch+
  133. # or +write+. To specify the threshold at which to compress values, set the
  134. # <tt>:compress_threshold</tt> option. The default threshold is 32K.
  135. class Store
  136. cattr_accessor :logger, :instance_writer => true
  137. attr_reader :silence, :options
  138. alias :silence? :silence
  139. # Create a new cache. The options will be passed to any write method calls except
  140. # for :namespace which can be used to set the global namespace for the cache.
  141. def initialize (options = nil)
  142. @options = options ? options.dup : {}
  143. end
  144. # Silence the logger.
  145. def silence!
  146. @silence = true
  147. self
  148. end
  149. # Silence the logger within a block.
  150. def mute
  151. previous_silence, @silence = defined?(@silence) && @silence, true
  152. yield
  153. ensure
  154. @silence = previous_silence
  155. end
  156. # Set to true if cache stores should be instrumented. Default is false.
  157. def self.instrument=(boolean)
  158. Thread.current[:instrument_cache_store] = boolean
  159. end
  160. def self.instrument
  161. Thread.current[:instrument_cache_store] || false
  162. end
  163. # Fetches data from the cache, using the given key. If there is data in
  164. # the cache with the given key, then that data is returned.
  165. #
  166. # If there is no such data in the cache (a cache miss), then nil will be
  167. # returned. However, if a block has been passed, that block will be run
  168. # in the event of a cache miss. The return value of the block will be
  169. # written to the cache under the given cache key, and that return value
  170. # will be returned.
  171. #
  172. # cache.write("today", "Monday")
  173. # cache.fetch("today") # => "Monday"
  174. #
  175. # cache.fetch("city") # => nil
  176. # cache.fetch("city") do
  177. # "Duckburgh"
  178. # end
  179. # cache.fetch("city") # => "Duckburgh"
  180. #
  181. # You may also specify additional options via the +options+ argument.
  182. # Setting <tt>:force => true</tt> will force a cache miss:
  183. #
  184. # cache.write("today", "Monday")
  185. # cache.fetch("today", :force => true) # => nil
  186. #
  187. # Setting <tt>:compress</tt> will store a large cache entry set by the call
  188. # in a compressed format.
  189. #
  190. #
  191. # Setting <tt>:expires_in</tt> will set an expiration time on the cache.
  192. # All caches support auto-expiring content after a specified number of
  193. # seconds. This value can be specified as an option to the constructor
  194. # (in which case all entries will be affected), or it can be supplied to
  195. # the +fetch+ or +write+ method to effect just one entry.
  196. #
  197. # cache = ActiveSupport::Cache::MemoryStore.new(:expires_in => 5.minutes)
  198. # cache.write(key, value, :expires_in => 1.minute) # Set a lower value for one entry
  199. #
  200. # Setting <tt>:race_condition_ttl</tt> is very useful in situations where a cache entry
  201. # is used very frequently and is under heavy load. If a cache expires and due to heavy load
  202. # seven different processes will try to read data natively and then they all will try to
  203. # write to cache. To avoid that case the first process to find an expired cache entry will
  204. # bump the cache expiration time by the value set in <tt>:race_condition_ttl</tt>. Yes
  205. # this process is extending the time for a stale value by another few seconds. Because
  206. # of extended life of the previous cache, other processes will continue to use slightly
  207. # stale data for a just a big longer. In the meantime that first process will go ahead
  208. # and will write into cache the new value. After that all the processes will start
  209. # getting new value. The key is to keep <tt>:race_condition_ttl</tt> small.
  210. #
  211. # If the process regenerating the entry errors out, the entry will be regenerated
  212. # after the specified number of seconds. Also note that the life of stale cache is
  213. # extended only if it expired recently. Otherwise a new value is generated and
  214. # <tt>:race_condition_ttl</tt> does not play any role.
  215. #
  216. # # Set all values to expire after one minute.
  217. # cache = ActiveSupport::Cache::MemoryCache.new(:expires_in => 1.minute)
  218. #
  219. # cache.write("foo", "original value")
  220. # val_1 = nil
  221. # val_2 = nil
  222. # sleep 60
  223. #
  224. # Thread.new do
  225. # val_1 = cache.fetch("foo", :race_condition_ttl => 10) do
  226. # sleep 1
  227. # "new value 1"
  228. # end
  229. # end
  230. #
  231. # Thread.new do
  232. # val_2 = cache.fetch("foo", :race_condition_ttl => 10) do
  233. # "new value 2"
  234. # end
  235. # end
  236. #
  237. # # val_1 => "new value 1"
  238. # # val_2 => "original value"
  239. # # sleep 10 # First thread extend the life of cache by another 10 seconds
  240. # # cache.fetch("foo") => "new value 1"
  241. #
  242. # Other options will be handled by the specific cache store implementation.
  243. # Internally, #fetch calls #read_entry, and calls #write_entry on a cache miss.
  244. # +options+ will be passed to the #read and #write calls.
  245. #
  246. # For example, MemCacheStore's #write method supports the +:raw+
  247. # option, which tells the memcached server to store all values as strings.
  248. # We can use this option with #fetch too:
  249. #
  250. # cache = ActiveSupport::Cache::MemCacheStore.new
  251. # cache.fetch("foo", :force => true, :raw => true) do
  252. # :bar
  253. # end
  254. # cache.fetch("foo") # => "bar"
  255. def fetch(name, options = nil)
  256. if block_given?
  257. options = merged_options(options)
  258. key = namespaced_key(name, options)
  259. unless options[:force]
  260. entry = instrument(:read, name, options) do |payload|
  261. payload[:super_operation] = :fetch if payload
  262. read_entry(key, options)
  263. end
  264. end
  265. if entry && entry.expired?
  266. race_ttl = options[:race_condition_ttl].to_f
  267. if race_ttl and Time.now.to_f - entry.expires_at <= race_ttl
  268. entry.expires_at = Time.now + race_ttl
  269. write_entry(key, entry, :expires_in => race_ttl * 2)
  270. else
  271. delete_entry(key, options)
  272. end
  273. entry = nil
  274. end
  275. if entry
  276. instrument(:fetch_hit, name, options) { |payload| }
  277. entry.value
  278. else
  279. result = instrument(:generate, name, options) do |payload|
  280. yield
  281. end
  282. write(name, result, options)
  283. result
  284. end
  285. else
  286. read(name, options)
  287. end
  288. end
  289. # Fetches data from the cache, using the given key. If there is data in
  290. # the cache with the given key, then that data is returned. Otherwise,
  291. # nil is returned.
  292. #
  293. # Options are passed to the underlying cache implementation.
  294. def read(name, options = nil)
  295. options = merged_options(options)
  296. key = namespaced_key(name, options)
  297. instrument(:read, name, options) do |payload|
  298. entry = read_entry(key, options)
  299. if entry
  300. if entry.expired?
  301. delete_entry(key, options)
  302. payload[:hit] = false if payload
  303. nil
  304. else
  305. payload[:hit] = true if payload
  306. entry.value
  307. end
  308. else
  309. payload[:hit] = false if payload
  310. nil
  311. end
  312. end
  313. end
  314. # Read multiple values at once from the cache. Options can be passed
  315. # in the last argument.
  316. #
  317. # Some cache implementation may optimize this method.
  318. #
  319. # Returns a hash mapping the names provided to the values found.
  320. def read_multi(*names)
  321. options = names.extract_options!
  322. options = merged_options(options)
  323. results = {}
  324. names.each do |name|
  325. key = namespaced_key(name, options)
  326. entry = read_entry(key, options)
  327. if entry
  328. if entry.expired?
  329. delete_entry(key, options)
  330. else
  331. results[name] = entry.value
  332. end
  333. end
  334. end
  335. results
  336. end
  337. # Writes the value to the cache, with the key.
  338. #
  339. # Options are passed to the underlying cache implementation.
  340. def write(name, value, options = nil)
  341. options = merged_options(options)
  342. instrument(:write, name, options) do |payload|
  343. entry = Entry.new(value, options)
  344. write_entry(namespaced_key(name, options), entry, options)
  345. end
  346. end
  347. # Deletes an entry in the cache. Returns +true+ if an entry is deleted.
  348. #
  349. # Options are passed to the underlying cache implementation.
  350. def delete(name, options = nil)
  351. options = merged_options(options)
  352. instrument(:delete, name) do |payload|
  353. delete_entry(namespaced_key(name, options), options)
  354. end
  355. end
  356. # Return true if the cache contains an entry for the given key.
  357. #
  358. # Options are passed to the underlying cache implementation.
  359. def exist?(name, options = nil)
  360. options = merged_options(options)
  361. instrument(:exist?, name) do |payload|
  362. entry = read_entry(namespaced_key(name, options), options)
  363. if entry && !entry.expired?
  364. true
  365. else
  366. false
  367. end
  368. end
  369. end
  370. # Delete all entries with keys matching the pattern.
  371. #
  372. # Options are passed to the underlying cache implementation.
  373. #
  374. # All implementations may not support this method.
  375. def delete_matched(matcher, options = nil)
  376. raise NotImplementedError.new("#{self.class.name} does not support delete_matched")
  377. end
  378. # Increment an integer value in the cache.
  379. #
  380. # Options are passed to the underlying cache implementation.
  381. #
  382. # All implementations may not support this method.
  383. def increment(name, amount = 1, options = nil)
  384. raise NotImplementedError.new("#{self.class.name} does not support increment")
  385. end
  386. # Increment an integer value in the cache.
  387. #
  388. # Options are passed to the underlying cache implementation.
  389. #
  390. # All implementations may not support this method.
  391. def decrement(name, amount = 1, options = nil)
  392. raise NotImplementedError.new("#{self.class.name} does not support decrement")
  393. end
  394. # Cleanup the cache by removing expired entries.
  395. #
  396. # Options are passed to the underlying cache implementation.
  397. #
  398. # All implementations may not support this method.
  399. def cleanup(options = nil)
  400. raise NotImplementedError.new("#{self.class.name} does not support cleanup")
  401. end
  402. # Clear the entire cache. Be careful with this method since it could
  403. # affect other processes if shared cache is being used.
  404. #
  405. # Options are passed to the underlying cache implementation.
  406. #
  407. # All implementations may not support this method.
  408. def clear(options = nil)
  409. raise NotImplementedError.new("#{self.class.name} does not support clear")
  410. end
  411. protected
  412. # Add the namespace defined in the options to a pattern designed to match keys.
  413. # Implementations that support delete_matched should call this method to translate
  414. # a pattern that matches names into one that matches namespaced keys.
  415. def key_matcher(pattern, options)
  416. prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
  417. if prefix
  418. source = pattern.source
  419. if source.start_with?('^')
  420. source = source[1, source.length]
  421. else
  422. source = ".*#{source[0, source.length]}"
  423. end
  424. Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
  425. else
  426. pattern
  427. end
  428. end
  429. # Read an entry from the cache implementation. Subclasses must implement this method.
  430. def read_entry(key, options) # :nodoc:
  431. raise NotImplementedError.new
  432. end
  433. # Write an entry to the cache implementation. Subclasses must implement this method.
  434. def write_entry(key, entry, options) # :nodoc:
  435. raise NotImplementedError.new
  436. end
  437. # Delete an entry from the cache implementation. Subclasses must implement this method.
  438. def delete_entry(key, options) # :nodoc:
  439. raise NotImplementedError.new
  440. end
  441. private
  442. # Merge the default options with ones specific to a method call.
  443. def merged_options(call_options) # :nodoc:
  444. if call_options
  445. options.merge(call_options)
  446. else
  447. options.dup
  448. end
  449. end
  450. # Expand key to be a consistent string value. Invoke +cache_key+ if
  451. # object responds to +cache_key+. Otherwise, to_param method will be
  452. # called. If the key is a Hash, then keys will be sorted alphabetically.
  453. def expanded_key(key) # :nodoc:
  454. return key.cache_key.to_s if key.respond_to?(:cache_key)
  455. case key
  456. when Array
  457. if key.size > 1
  458. key = key.collect{|element| expanded_key(element)}
  459. else
  460. key = key.first
  461. end
  462. when Hash
  463. key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
  464. end
  465. key.to_param
  466. end
  467. # Prefix a key with the namespace. Namespace and key will be delimited with a colon.
  468. def namespaced_key(key, options)
  469. key = expanded_key(key)
  470. namespace = options[:namespace] if options
  471. prefix = namespace.is_a?(Proc) ? namespace.call : namespace
  472. key = "#{prefix}:#{key}" if prefix
  473. key
  474. end
  475. def instrument(operation, key, options = nil)
  476. log(operation, key, options)
  477. if self.class.instrument
  478. payload = { :key => key }
  479. payload.merge!(options) if options.is_a?(Hash)
  480. ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
  481. else
  482. yield(nil)
  483. end
  484. end
  485. def log(operation, key, options = nil)
  486. return unless logger && logger.debug? && !silence?
  487. logger.debug("Cache #{operation}: #{key}#{options.blank? ? "" : " (#{options.inspect})"}")
  488. end
  489. end
  490. # Entry that is put into caches. It supports expiration time on entries and can compress values
  491. # to save space in the cache.
  492. class Entry
  493. attr_reader :created_at, :expires_in
  494. DEFAULT_COMPRESS_LIMIT = 16.kilobytes
  495. class << self
  496. # Create an entry with internal attributes set. This method is intended to be
  497. # used by implementations that store cache entries in a native format instead
  498. # of as serialized Ruby objects.
  499. def create (raw_value, created_at, options = {})
  500. entry = new(nil)
  501. entry.instance_variable_set(:@value, raw_value)
  502. entry.instance_variable_set(:@created_at, created_at.to_f)
  503. entry.instance_variable_set(:@compressed, !!options[:compressed])
  504. entry.instance_variable_set(:@expires_in, options[:expires_in])
  505. entry
  506. end
  507. end
  508. # Create a new cache entry for the specified value. Options supported are
  509. # +:compress+, +:compress_threshold+, and +:expires_in+.
  510. def initialize(value, options = {})
  511. @compressed = false
  512. @expires_in = options[:expires_in]
  513. @expires_in = @expires_in.to_f if @expires_in
  514. @created_at = Time.now.to_f
  515. if value.nil?
  516. @value = nil
  517. else
  518. @value = Marshal.dump(value)
  519. if should_compress?(value, options)
  520. @value = Zlib::Deflate.deflate(@value)
  521. @compressed = true
  522. end
  523. end
  524. end
  525. # Get the raw value. This value may be serialized and compressed.
  526. def raw_value
  527. @value
  528. end
  529. # Get the value stored in the cache.
  530. def value
  531. if @value
  532. Marshal.load(compressed? ? Zlib::Inflate.inflate(@value) : @value)
  533. end
  534. end
  535. def compressed?
  536. @compressed
  537. end
  538. # Check if the entry is expired. The +expires_in+ parameter can override the
  539. # value set when the entry was created.
  540. def expired?
  541. @expires_in && @created_at + @expires_in <= Time.now.to_f
  542. end
  543. # Set a new time when the entry will expire.
  544. def expires_at=(time)
  545. if time
  546. @expires_in = time.to_f - @created_at
  547. else
  548. @expires_in = nil
  549. end
  550. end
  551. # Seconds since the epoch when the entry will expire.
  552. def expires_at
  553. @expires_in ? @created_at + @expires_in : nil
  554. end
  555. # Returns the size of the cached value. This could be less than value.size
  556. # if the data is compressed.
  557. def size
  558. if @value.nil?
  559. 0
  560. else
  561. @value.bytesize
  562. end
  563. end
  564. private
  565. def should_compress?(value, options)
  566. if options[:compress] && value
  567. unless value.is_a?(Numeric)
  568. compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
  569. serialized_value = value.is_a?(String) ? value : Marshal.dump(value)
  570. return true if serialized_value.size >= compress_threshold
  571. end
  572. end
  573. false
  574. end
  575. end
  576. end
  577. end