Module: Interlock
- Defined in:
- lib/interlock.rb,
lib/interlock/config.rb,
lib/interlock/interlock.rb
Defined Under Namespace
Modules: Config Classes: DependencyError, FragmentConsistencyError, InterlockError, UsageError
Constant Summary collapse
- DEFAULTS =
{ :ttl => 1.day, :namespace => 'app', :servers => ['localhost:11211'] }
- SCOPE_KEYS =
[:controller, :action, :id]
- KEY_LENGTH_LIMIT =
Buried value extracted from memcache.rb in the memcache-client gem. If one tries to request a key that is too long, the client throws an error. In practice, it seems better to simply avoid ever setting such long keys, so we use this value to achieve such for keys generated by Interlock.
250
- ILLEGAL_KEY_CHARACTERS_PATTERN =
Similarly buried and useful: no whitespace allowed in keys.
/\s/
- @@config =
DEFAULTS
Class Method Summary collapse
-
.caching_key(controller, action, id, tag) ⇒ Object
Build a fragment key for an explicitly passed context.
-
.dependency_key(klass) ⇒ Object
Get the Memcached key for a class’s dependency list.
-
.extract_options_and_dependencies(dependencies, default = nil) ⇒ Object
Extract the dependencies from the rest of the arguments and registers them with the appropriate models.
-
.invalidate(key) ⇒ Object
Invalidate a particular key.
-
.register_dependencies(dependencies, key) ⇒ Object
Add each key with scope to the appropriate dependencies array.
-
.say(key, msg) ⇒ Object
:nodoc:.
Class Method Details
.caching_key(controller, action, id, tag) ⇒ Object
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.
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# File 'lib/interlock/interlock.rb', line 106 def caching_key(controller, action, id, tag) raise ArgumentError, 'Both controller and action must be specified' unless controller and action id = (id or 'all').to_interlock_tag tag = tag.to_interlock_tag key = "interlock:#{ENV['RAILS_ASSET_ID']}:#{controller}:#{action}:#{id}:#{tag}" if key.length > KEY_LENGTH_LIMIT old_key = key key = key[0..KEY_LENGTH_LIMIT-1] say old_key, "truncated to #{key}" end key.gsub ILLEGAL_KEY_CHARACTERS_PATTERN, '' end |
.dependency_key(klass) ⇒ Object
Get the Memcached key for a class’s dependency list. We store per-class to reduce lock contention.
96 97 98 |
# File 'lib/interlock/interlock.rb', line 96 def dependency_key(klass) "interlock:#{ENV['RAILS_ASSET_ID']}:dependency:#{klass.name}" end |
.extract_options_and_dependencies(dependencies, default = nil) ⇒ Object
Extract the dependencies from the rest of the arguments and registers them with the appropriate models.
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/interlock/interlock.rb', line 31 def (dependencies, default = nil) = dependencies. # Hook up the dependencies nested array. dependencies.map! { |klass| [klass, :all] } .each do |klass, scope| if klass.is_a? Class # # Beware! Scoping to :id means that a request's params[:id] must equal # klass#id or the rule will not trigger. This is because params[:id] is the # only record-specific scope include in the key. # # If you want fancier invalidation, think hard about whether it really # matters. Over-invalidation is rarely a problem, but under-invalidation # frequently is. # # "But I need it!" you say. All right, then start using key tags. # raise Interlock::DependencyError, "#{scope.inspect} is not a valid scope" unless [:all, :id].include?(scope) dependencies << [klass, scope.to_sym] end end unless dependencies.any? # Use the conventional controller/model association if none are provided # Can be skipped by calling caching(nil) dependencies = [[default, :all]] end # Remove nils dependencies.reject! {|klass, scope| klass.nil? } [.indifferentiate, dependencies] end |
.invalidate(key) ⇒ Object
Invalidate a particular key.
126 127 128 |
# File 'lib/interlock/interlock.rb', line 126 def invalidate(key) ActionController::Base.fragment_cache_store.delete key end |
.register_dependencies(dependencies, key) ⇒ Object
Add each key with scope to the appropriate dependencies array.
69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/interlock/interlock.rb', line 69 def register_dependencies(dependencies, key) Array(dependencies).each do |klass, scope| dep_key = dependency_key(klass) # Get the value for this class/key out of the global store. this = (CACHE.get(dep_key) || {})[key] # Make sure to not overwrite broader scopes. unless this == :all or this == scope # We need to write, so acquire the lock. CACHE.lock(dep_key) do |hash| Interlock.say key, "registered a dependency on #{klass} -> #{scope.inspect}." (hash || {}).merge({key => scope}) end end end end |
.say(key, msg) ⇒ Object
:nodoc:
88 89 90 |
# File 'lib/interlock/interlock.rb', line 88 def say(key, msg) #:nodoc: RAILS_DEFAULT_LOGGER.warn "** fragment #{key.inspect[1..-2]} #{msg}" end |