Module: Interlock
require ‘ehcache‘
Child modules and classes
Module Interlock::Config
Module Interlock::Finders
Module Interlock::Lock
Constants
| Name | Value |
|---|---|
| DEFAULTS | { :ttl => 1.day, :namespace => 'app', :servers => ['127.0.0.1:11211'], :client => 'memcache-client', :with_finders => false |
| CLIENT_KEYS | [ :prefix_key, :distribution, :verify_key, :tcp_nodelay, :hash, :hash_with_prefix_key, :show_backtraces, :default_ttl, :ketama_weighted, :retry_timeout, :default_weight, :buffer_requests, :timeout, :sort_hosts, :cache_lookups, :connect_timeout, :no_block, :failover, :support_cas, :namespace |
| SCOPE_KEYS | [:controller, :action, :id] |
| KEY_LENGTH_LIMIT | 250 |
| ILLEGAL_KEY_CHARACTERS_PATTERN | /\s/ |
Public Class Methods
caching_key (controller, action, id, tag)
Build a fragment key for an explicitly passed context. Shouldn‘t be called unless you need to write your own fine-grained invalidation rules. Make sure the default ones are really unacceptable before you go to the trouble of rolling your own.
# File lib/interlock/interlock.rb, line 128 128: def caching_key(controller, action, id, tag) 129: raise ArgumentError, 'Both controller and action must be specified' unless controller and action 130: 131: id = (id or 'all').to_interlock_tag 132: tag = tag.to_interlock_tag 133: 134: key = "interlock:#{ENV['RAILS_ASSET_ID']}:#{controller}:#{action}:#{id}:#{tag}" 135: 136: if key.length > KEY_LENGTH_LIMIT 137: old_key = key 138: key = key[0..KEY_LENGTH_LIMIT-1] 139: say old_key, "truncated to #{key}" 140: end 141: 142: key.gsub ILLEGAL_KEY_CHARACTERS_PATTERN, '' 143: end
dependency_key (klass, scope, key)
Get the Memcached key for a class‘s dependency list. We store per-class to reduce lock contention.
# File lib/interlock/interlock.rb, line 117 117: def dependency_key(klass, scope, key) 118: id = (scope == :id ? ":#{key.field(4)}" : nil) 119: "interlock:#{ENV['RAILS_ASSET_ID']}:dependency:#{klass.name}#{id}" 120: end
extract_options_and_dependencies (dependencies, default = nil)
Extract the dependencies from the rest of the arguments and registers them with the appropriate models.
# File lib/interlock/interlock.rb, line 40 40: def extract_options_and_dependencies(dependencies, default = nil) 41: options = dependencies.extract_options! 42: 43: # Hook up the dependencies nested array. 44: dependencies.map! { |klass| [klass, :all] } 45: options.each do |klass, scope| 46: if klass.is_a? Class 47: # 48: # Beware! Scoping to :id means that a request's params[:id] must equal 49: # klass#id or the rule will not trigger. This is because params[:id] is the 50: # only record-specific scope include in the key. 51: # 52: # If you want fancier invalidation, think hard about whether it really 53: # matters. Over-invalidation is rarely a problem, but under-invalidation 54: # frequently is. 55: # 56: # "But I need it!" you say. All right, then start using key tags. 57: # 58: raise Interlock::DependencyError, "#{scope.inspect} is not a valid scope" unless [:all, :id].include?(scope) 59: dependencies << [klass, scope.to_sym] 60: end 61: end 62: 63: unless dependencies.any? 64: # Use the conventional controller/model association if none are provided 65: # Can be skipped by calling caching(nil) 66: dependencies = [[default, :all]] 67: end 68: 69: # Remove nils 70: dependencies.reject! {|klass, scope| klass.nil? } 71: 72: [options.indifferentiate, dependencies] 73: end
invalidate (key)
Invalidate a particular key.
# File lib/interlock/interlock.rb, line 148 148: def invalidate(key) 149: # Console and tests do not install the local cache 150: Interlock.local_cache.delete(key) if Interlock.local_cache 151: ActionController::Base.cache_store.delete key 152: end
log (msg)
# File lib/interlock/interlock.rb, line 102 102: def log(msg) 103: case Interlock.config[:log_level] 104: when 'debug', 'info', 'warn', 'error' 105: log_method = Interlock.config[:log_level] 106: else 107: log_method = :debug 108: end 109: 110: RAILS_DEFAULT_LOGGER.send( log_method, msg ) 111: end
register_dependencies (dependencies, key)
Add each key with scope to the appropriate dependencies array.
# File lib/interlock/interlock.rb, line 78 78: def register_dependencies(dependencies, key) 79: return if Interlock.config[:disabled] 80: 81: Array(dependencies).each do |klass, scope| 82: dep_key = dependency_key(klass, scope, key) 83: 84: # Get the value for this class/key out of the global store. 85: this = (CACHE.get(dep_key) || {})[key] 86: 87: # Make sure to not overwrite broader scopes. 88: unless this == :all or this == scope 89: # We need to write, so acquire the lock. 90: CACHE.lock(dep_key) do |hash| 91: Interlock.say key, "registered a dependency on #{klass} -> #{scope.inspect}." 92: (hash || {}).merge({key => scope}) 93: end 94: end 95: end 96: end