interlock 1.1 → 1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +2 -0
- data/Manifest +2 -2
- data/README +10 -5
- data/examples/memcached.yml +2 -1
- data/interlock.gemspec +16 -15
- data/lib/interlock/action_controller.rb +33 -25
- data/lib/interlock/config.rb +61 -6
- data/lib/interlock/core_extensions.rb +14 -4
- data/lib/interlock/interlock.rb +4 -0
- data/lib/interlock/lock.rb +43 -0
- data/lib/interlock.rb +6 -9
- data/test/integration/app/config/environments/development.rb +6 -4
- data/test/integration/app/config/memcached.yml +3 -3
- data/test/unit/{memcached_test.rb → lock_test.rb} +2 -2
- data.tar.gz.sig +0 -0
- metadata +7 -15
- metadata.gz.sig +0 -0
- data/lib/interlock/memcached.rb +0 -30
data/CHANGELOG
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
v1.2. Add compatibility with Memcached client.
|
3
|
+
|
2
4
|
v1.1. Add :perform => false option. Cache :content_for. Test nested view_cache blocks. Don't assign conventional dependency when called from the view, because we don't know what the controller might have already specified.
|
3
5
|
|
4
6
|
v1.0. First release.
|
data/Manifest
CHANGED
@@ -7,7 +7,7 @@ lib/interlock/active_record.rb
|
|
7
7
|
lib/interlock/config.rb
|
8
8
|
lib/interlock/core_extensions.rb
|
9
9
|
lib/interlock/interlock.rb
|
10
|
-
lib/interlock/
|
10
|
+
lib/interlock/lock.rb
|
11
11
|
lib/interlock.rb
|
12
12
|
LICENSE
|
13
13
|
Manifest
|
@@ -78,5 +78,5 @@ test/setup.rb
|
|
78
78
|
test/teardown.rb
|
79
79
|
test/test_helper.rb
|
80
80
|
test/unit/interlock_test.rb
|
81
|
-
test/unit/
|
81
|
+
test/unit/lock_test.rb
|
82
82
|
TODO
|
data/README
CHANGED
@@ -5,9 +5,11 @@ A Rails plugin for maintainable and high-efficiency caching.
|
|
5
5
|
|
6
6
|
== License
|
7
7
|
|
8
|
-
Copyright 2007 Cloudburst, LLC. Licensed under the AFL 3
|
8
|
+
Copyright 2007, 2008 Cloudburst, LLC. Licensed under the AFL 3. See the included LICENSE file. Portions copyright 2006 Chris Wanstrath and used with permission.
|
9
9
|
|
10
|
-
The public certificate for the gem is
|
10
|
+
The public certificate for the gem is here[http://rubyforge.org/frs/download.php/25331/evan_weaver-original-public_cert.pem].
|
11
|
+
|
12
|
+
If you use this software, please {make a donation}[http://blog.evanweaver.com/donate/], or {recommend Evan}[http://www.workingwithrails.com/person/7739-evan-weaver] at Working with Rails.
|
11
13
|
|
12
14
|
== Requirements
|
13
15
|
|
@@ -27,7 +29,7 @@ Interlock automatically tracks invalidation dependencies based on the model life
|
|
27
29
|
|
28
30
|
First, compile and install memcached itself. Get a memcached server running.
|
29
31
|
|
30
|
-
You also need
|
32
|
+
You also need either <tt>memcache-client</tt> or {memcached}[http://blog.evanweaver.com/files/doc/fauna/memcached]:
|
31
33
|
sudo gem install memcache-client
|
32
34
|
|
33
35
|
Then, install the plugin:
|
@@ -38,15 +40,18 @@ Lastly, configure your Rails app for memcached by creating a <tt>config/memcache
|
|
38
40
|
defaults:
|
39
41
|
namespace: myapp
|
40
42
|
sessions: false
|
43
|
+
client: memcache-client
|
41
44
|
development:
|
42
45
|
servers:
|
43
|
-
-
|
46
|
+
- 127.0.0.1:11211 # Default host and port
|
44
47
|
production:
|
45
48
|
servers
|
46
49
|
- 10.12.128.1
|
47
50
|
- 10.12.128.2
|
48
51
|
|
49
52
|
Now you're ready to go.
|
53
|
+
|
54
|
+
Note that if you have {memcached 0.7}[http://blog.evanweaver.com/files/doc/fauna/memcached], you can use <tt>client: memcached</tt> for better performance.
|
50
55
|
|
51
56
|
== Usage
|
52
57
|
|
@@ -106,6 +111,6 @@ Also, Interlock obeys the <tt>ENV['RAILS_ASSET_ID']</tt> setting, so if you need
|
|
106
111
|
|
107
112
|
== Reporting problems
|
108
113
|
|
109
|
-
|
114
|
+
The support forum is here[http://rubyforge.org/forum/forum.php?forum_id=19835].
|
110
115
|
|
111
116
|
Patches and contributions are very welcome. Please note that contributors are required to assign copyright for their additions to Cloudburst, LLC.
|
data/examples/memcached.yml
CHANGED
data/interlock.gemspec
CHANGED
@@ -1,28 +1,26 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Interlock-1.
|
2
|
+
# Gem::Specification for Interlock-1.2
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{interlock}
|
7
|
-
s.version = "1.
|
7
|
+
s.version = "1.2"
|
8
8
|
|
9
9
|
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.authors = [""]
|
13
|
-
s.date = %q{2008-
|
13
|
+
s.date = %q{2008-02-04}
|
14
14
|
s.description = %q{A Rails plugin for maintainable and high-efficiency caching.}
|
15
15
|
s.email = %q{}
|
16
|
-
s.files = ["CHANGELOG", "examples/memcached.yml", "init.rb", "lib/interlock/action_controller.rb", "lib/interlock/action_view.rb", "lib/interlock/active_record.rb", "lib/interlock/config.rb", "lib/interlock/core_extensions.rb", "lib/interlock/interlock.rb", "lib/interlock/
|
16
|
+
s.files = ["CHANGELOG", "examples/memcached.yml", "init.rb", "lib/interlock/action_controller.rb", "lib/interlock/action_view.rb", "lib/interlock/active_record.rb", "lib/interlock/config.rb", "lib/interlock/core_extensions.rb", "lib/interlock/interlock.rb", "lib/interlock/lock.rb", "lib/interlock.rb", "LICENSE", "Manifest", "README", "tasks/interlock.rake", "test/integration/app/app/controllers/application.rb", "test/integration/app/app/controllers/eval_controller.rb", "test/integration/app/app/controllers/items_controller.rb", "test/integration/app/app/helpers/application_helper.rb", "test/integration/app/app/helpers/eval_helper.rb", "test/integration/app/app/helpers/items_helper.rb", "test/integration/app/app/models/item.rb", "test/integration/app/app/views/items/detail.rhtml", "test/integration/app/app/views/items/list.rhtml", "test/integration/app/app/views/items/recent.rhtml", "test/integration/app/app/views/items/show.rhtml", "test/integration/app/app/views/layouts/application.html.erb", "test/integration/app/app/views/shared/_related.rhtml", "test/integration/app/config/boot.rb", "test/integration/app/config/database.yml", "test/integration/app/config/environment.rb", "test/integration/app/config/environments/development.rb", "test/integration/app/config/environments/production.rb", "test/integration/app/config/environments/test.rb", "test/integration/app/config/initializers/inflections.rb", "test/integration/app/config/initializers/mime_types.rb", "test/integration/app/config/memcached.yml", "test/integration/app/config/routes.rb", "test/integration/app/db/migrate/001_create_items.rb", "test/integration/app/doc/README_FOR_APP", "test/integration/app/public/404.html", "test/integration/app/public/422.html", "test/integration/app/public/500.html", "test/integration/app/public/dispatch.cgi", "test/integration/app/public/dispatch.fcgi", "test/integration/app/public/dispatch.rb", "test/integration/app/public/favicon.ico", "test/integration/app/public/images/rails.png", "test/integration/app/public/index.html", "test/integration/app/public/javascripts/application.js", "test/integration/app/public/javascripts/controls.js", "test/integration/app/public/javascripts/dragdrop.js", "test/integration/app/public/javascripts/effects.js", "test/integration/app/public/javascripts/prototype.js", "test/integration/app/public/robots.txt", "test/integration/app/Rakefile", "test/integration/app/README", "test/integration/app/script/about", "test/integration/app/script/console", "test/integration/app/script/destroy", "test/integration/app/script/generate", "test/integration/app/script/performance/benchmarker", "test/integration/app/script/performance/profiler", "test/integration/app/script/performance/request", "test/integration/app/script/plugin", "test/integration/app/script/process/inspector", "test/integration/app/script/process/reaper", "test/integration/app/script/process/spawner", "test/integration/app/script/runner", "test/integration/app/script/server", "test/integration/app/test/fixtures/items.yml", "test/integration/app/test/functional/eval_controller_test.rb", "test/integration/app/test/functional/items_controller_test.rb", "test/integration/app/test/test_helper.rb", "test/integration/app/test/unit/item_test.rb", "test/integration/server_test.rb", "test/setup.rb", "test/teardown.rb", "test/test_helper.rb", "test/unit/interlock_test.rb", "test/unit/lock_test.rb", "TODO", "interlock.gemspec"]
|
17
17
|
s.has_rdoc = true
|
18
18
|
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/interlock/}
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
s.rubyforge_project = %q{fauna}
|
21
21
|
s.rubygems_version = %q{1.0.1}
|
22
22
|
s.summary = %q{A Rails plugin for maintainable and high-efficiency caching.}
|
23
|
-
s.test_files = ["test/integration/server_test.rb", "test/unit/interlock_test.rb", "test/unit/
|
24
|
-
|
25
|
-
s.add_dependency(%q<memcache_client>, [">= 1.5.0"])
|
23
|
+
s.test_files = ["test/integration/server_test.rb", "test/unit/interlock_test.rb", "test/unit/lock_test.rb"]
|
26
24
|
end
|
27
25
|
|
28
26
|
|
@@ -36,7 +34,6 @@ end
|
|
36
34
|
# p.summary = "A Rails plugin for maintainable and high-efficiency caching."
|
37
35
|
# p.url = "http://blog.evanweaver.com/files/doc/fauna/interlock/"
|
38
36
|
# p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
|
39
|
-
# p.dependencies = "memcache_client >=1.5.0"
|
40
37
|
# p.test_pattern = ["test/integration/*.rb", "test/unit/*.rb"]
|
41
38
|
# p.rdoc_pattern = ["README", "CHANGELOG", "TODO", "LICENSE", "lib/interlock/memcached.rb", "lib/interlock/interlock.rb", "lib/interlock/action_controller.rb", "lib/interlock/active_record.rb", "lib/interlock/action_view.rb", "lib/interlock/config.rb"]
|
42
39
|
# p.clean_pattern += ['test/integration/app/coverage', 'test/integration/app/db/schema.rb',
|
@@ -44,13 +41,17 @@ end
|
|
44
41
|
# end
|
45
42
|
#
|
46
43
|
# desc "Run all the tests in production and development mode both"
|
47
|
-
# task "test_all" do
|
48
|
-
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
44
|
+
# task "test_all" do
|
45
|
+
# ['memcache-client', 'memcached'].each do |client|
|
46
|
+
# ENV['CLIENT'] = client
|
47
|
+
# ENV['PRODUCTION'] = 'false'
|
48
|
+
# STDERR.puts "#{'='*80}\nDevelopment mode: #{client}\n#{'='*80}"
|
49
|
+
# system("rake test:multi_rails:all")
|
50
|
+
#
|
51
|
+
# ENV['PRODUCTION'] = 'true'
|
52
|
+
# STDERR.puts "#{'='*80}\nProduction mode: #{client}\n#{'='*80}"
|
53
|
+
# system("rake test:multi_rails:all")
|
54
|
+
# end
|
54
55
|
# end
|
55
56
|
#
|
56
57
|
# task "tail" do
|
@@ -189,33 +189,41 @@ And in the <tt>show.html.erb</tt> view:
|
|
189
189
|
#
|
190
190
|
def read_fragment(key, options = nil)
|
191
191
|
return unless perform_caching
|
192
|
-
|
193
|
-
if content = Interlock.local_cache.read(key, options)
|
194
|
-
# Interlock.say key, "read from local cache"
|
195
|
-
elsif content = fragment_cache_store.read(key, options)
|
196
|
-
raise FragmentConsistencyError, "#{key} is not an Array" unless content.is_a? Array
|
197
|
-
Interlock.say key, "read from memcached"
|
198
|
-
Interlock.local_cache.write(key, content, options)
|
199
|
-
else
|
200
|
-
# Not found
|
201
|
-
return nil
|
202
|
-
end
|
203
192
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
#
|
213
|
-
|
193
|
+
begin
|
194
|
+
if content = Interlock.local_cache.read(key, options)
|
195
|
+
# Interlock.say key, "read from local cache"
|
196
|
+
elsif content = fragment_cache_store.read(key, options)
|
197
|
+
raise Interlock::FragmentConsistencyError, "#{key} expected Array but got #{content.class}" unless content.is_a? Array
|
198
|
+
Interlock.say key, "read from memcached"
|
199
|
+
Interlock.local_cache.write(key, content, options)
|
200
|
+
else
|
201
|
+
# Not found
|
202
|
+
return nil
|
214
203
|
end
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
204
|
+
|
205
|
+
raise Interlock::FragmentConsistencyError, "#{key}::content expected String but got #{content.first.class}" unless content.first.is_a? String
|
206
|
+
|
207
|
+
options ||= {}
|
208
|
+
# Note that 'nil' is considered true for :assign_content_for
|
209
|
+
if options[:assign_content_for] != false and content.last
|
210
|
+
# Extract content_for variables
|
211
|
+
content.last.each do |name, value|
|
212
|
+
raise Interlock::FragmentConsistencyError, "#{key}::content_for(:#{name}) expected String but got #{value.class}" unless value.is_a? String
|
213
|
+
# We'll just call the helper because that will handle nested view_caches properly.
|
214
|
+
@template.send(:content_for, name, value)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
content.first
|
219
|
+
rescue Interlock::FragmentConsistencyError => e
|
220
|
+
# XXX Needs test coverage
|
221
|
+
# Delete the bogus key
|
222
|
+
begin; CACHE.delete key; rescue; end
|
223
|
+
# Reraise the error
|
224
|
+
raise e
|
225
|
+
end
|
226
|
+
end
|
219
227
|
|
220
228
|
end
|
221
229
|
end
|
data/lib/interlock/config.rb
CHANGED
@@ -4,8 +4,19 @@ module Interlock
|
|
4
4
|
DEFAULTS = {
|
5
5
|
:ttl => 1.day,
|
6
6
|
:namespace => 'app',
|
7
|
-
:servers => ['
|
7
|
+
:servers => ['127.0.0.1:11211'],
|
8
|
+
:client => 'memcache-client'
|
8
9
|
}
|
10
|
+
|
11
|
+
CLIENT_KEYS = [ #:nodoc:
|
12
|
+
:hash,
|
13
|
+
:no_block,
|
14
|
+
:buffer_requests,
|
15
|
+
:support_cas,
|
16
|
+
:tcp_nodelay,
|
17
|
+
:distribution,
|
18
|
+
:namespace
|
19
|
+
]
|
9
20
|
|
10
21
|
mattr_accessor :config
|
11
22
|
@@config = DEFAULTS
|
@@ -23,7 +34,8 @@ module Interlock
|
|
23
34
|
#
|
24
35
|
def run!
|
25
36
|
if File.exist?(CONFIG_FILE)
|
26
|
-
|
37
|
+
template = ERB.new(File.open(CONFIG_FILE) {|f| f.read})
|
38
|
+
config = YAML.load(template.result(binding))
|
27
39
|
config.deep_symbolize_keys!
|
28
40
|
|
29
41
|
Interlock.config.merge!(config[:defaults] || {})
|
@@ -41,12 +53,47 @@ module Interlock
|
|
41
53
|
Interlock.config[:namespace] << "-#{RAILS_ENV}"
|
42
54
|
|
43
55
|
unless defined? Object::CACHE
|
44
|
-
|
45
|
-
|
46
|
-
|
56
|
+
|
57
|
+
# Give people a choice of client, even though I don't like conditional dependencies.
|
58
|
+
klass = case Interlock.config[:client]
|
59
|
+
when 'memcached'
|
60
|
+
Memcached::Rails
|
61
|
+
when 'memcache-client'
|
62
|
+
raise ConfigurationError, "You have the Ruby-MemCache gem installed. Please uninstall Ruby-MemCache, or otherwise guarantee that memcache-client will load instead." if MemCache.constants.include?('SVNURL')
|
63
|
+
MemCache
|
64
|
+
else
|
65
|
+
raise ConfigurationError, "Invalid client name '#{Interlock.config[:client]}'"
|
66
|
+
end
|
67
|
+
|
68
|
+
Object.const_set('CACHE',
|
69
|
+
klass.new(
|
70
|
+
Interlock.config[:servers],
|
71
|
+
Interlock.config.slice(*CLIENT_KEYS)
|
72
|
+
)
|
73
|
+
)
|
74
|
+
|
75
|
+
# Mark that we're the ones who did it.
|
76
|
+
class << CACHE
|
77
|
+
def installed_by_interlock; true; end
|
78
|
+
end
|
79
|
+
|
80
|
+
else
|
81
|
+
begin
|
82
|
+
CACHE.installed_by_interlock
|
83
|
+
rescue NoMethodError
|
84
|
+
RAILS_DEFAULT_LOGGER.warn "** interlock: Object::CACHE already defined; will not install a new one"
|
85
|
+
# Mark that somebody else installed this CACHE.
|
86
|
+
class << CACHE
|
87
|
+
def installed_by_interlock; false; end
|
88
|
+
end
|
89
|
+
end
|
47
90
|
end
|
48
91
|
|
92
|
+
# Add the fragment cache and lock APIs to the cache singleton. This happens no matter
|
93
|
+
# who installed the singleton.
|
49
94
|
class << CACHE
|
95
|
+
include Interlock::Lock
|
96
|
+
|
50
97
|
def read(*args)
|
51
98
|
get args.first
|
52
99
|
end
|
@@ -64,9 +111,17 @@ module Interlock
|
|
64
111
|
# Configure Rails to use the memcached store for fragments, and optionally, sessions.
|
65
112
|
#
|
66
113
|
def rails!
|
67
|
-
# Memcached fragment caching is mandatory
|
114
|
+
# Memcached fragment caching is mandatory
|
115
|
+
ActionView::Helpers::CacheHelper.class_eval do
|
116
|
+
def cache(name, options = nil, &block)
|
117
|
+
# Things explode if options does not default to nil
|
118
|
+
RAILS_DEFAULT_LOGGER.debug "** fragment #{name} stored via obsolete cache() call"
|
119
|
+
@controller.cache_erb_fragment(block, name, options)
|
120
|
+
end
|
121
|
+
end
|
68
122
|
ActionController::Base.fragment_cache_store = CACHE
|
69
123
|
|
124
|
+
# Sessions are optional
|
70
125
|
if Interlock.config[:sessions]
|
71
126
|
ActionController::Base.session_store = :mem_cache_store
|
72
127
|
ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS.update 'cache' => CACHE
|
@@ -40,16 +40,26 @@ class Hash
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
# Compatibility method for Rails 1.2.6. It's also faster.
|
44
|
+
unless Hash.instance_methods.include? "slice"
|
45
|
+
def slice(*keys)
|
46
|
+
hash = {}
|
47
|
+
keys.each do |key|
|
48
|
+
hash[key] = self[key] if self[key]
|
49
|
+
end
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
43
54
|
end
|
44
55
|
|
45
56
|
class Array
|
46
|
-
|
47
|
-
|
57
|
+
# Compatibility method for Rails 1.2.6.
|
58
|
+
unless Array.instance_methods.include? "extract_options!"
|
48
59
|
def extract_options!
|
49
60
|
# Method added in Rails rev 7217
|
50
61
|
last.is_a?(Hash) ? pop : {}
|
51
|
-
end
|
52
|
-
|
62
|
+
end
|
53
63
|
end
|
54
64
|
end
|
55
65
|
|
data/lib/interlock/interlock.rb
CHANGED
@@ -5,8 +5,12 @@ module Interlock
|
|
5
5
|
end
|
6
6
|
class DependencyError < InterlockError #:nodoc:
|
7
7
|
end
|
8
|
+
class ConfigurationError < InterlockError #:nodoc:
|
9
|
+
end
|
8
10
|
class UsageError < InterlockError #:nodoc:
|
9
11
|
end
|
12
|
+
class LockAcquisitionError < InterlockError #:nodoc:
|
13
|
+
end
|
10
14
|
class FragmentConsistencyError < InterlockError #:nodoc:
|
11
15
|
end
|
12
16
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
|
2
|
+
module Interlock
|
3
|
+
module Lock
|
4
|
+
|
5
|
+
#
|
6
|
+
# Try to acquire a global lock from memcached for a particular key.
|
7
|
+
# If successful, yield and set the key to the return value, then release
|
8
|
+
# the lock.
|
9
|
+
#
|
10
|
+
# Based on http://rubyurl.com/Sw7 , which I partially wrote.
|
11
|
+
#
|
12
|
+
|
13
|
+
def lock(key, lock_expiry = 30, retries = 5)
|
14
|
+
retries.times do |count|
|
15
|
+
|
16
|
+
# We have to be compatible with both client APIs. Eventually we can use Memcached#cas
|
17
|
+
# for this.
|
18
|
+
begin
|
19
|
+
response = CACHE.add("lock:#{key}", "Locked by #{Process.pid}", lock_expiry)
|
20
|
+
# Nil is a successful response for Memcached, so we'll simulate the MemCache
|
21
|
+
# API.
|
22
|
+
response ||= "STORED\r\n"
|
23
|
+
rescue Object => e
|
24
|
+
# Catch exceptions from Memcached without setting response.
|
25
|
+
end
|
26
|
+
|
27
|
+
if response == "STORED\r\n"
|
28
|
+
begin
|
29
|
+
value = yield(CACHE.get(key))
|
30
|
+
CACHE.set(key, value)
|
31
|
+
return value
|
32
|
+
ensure
|
33
|
+
CACHE.delete("lock:#{key}")
|
34
|
+
end
|
35
|
+
else
|
36
|
+
sleep((2**count) / 2.0)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
raise ::Interlock::LockAcquisitionError, "Couldn't acquire lock for #{key}"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
data/lib/interlock.rb
CHANGED
@@ -2,22 +2,19 @@
|
|
2
2
|
module Interlock
|
3
3
|
end
|
4
4
|
|
5
|
-
unless defined? MemCache or defined? MemCacheWithConsistentHashing
|
6
|
-
raise "Interlock requires the memcache-client gem"
|
7
|
-
end
|
8
|
-
|
9
|
-
if MemCache.constants.include?('SVNURL')
|
10
|
-
raise "You have the Ruby-MemCache gem installed. Interlock uses memcache-client. Please uninstall Ruby-MemCache, or otherwise guarantee that memcache-client will load instead."
|
11
|
-
end
|
12
|
-
|
13
5
|
require 'interlock/core_extensions'
|
14
6
|
require 'interlock/config'
|
15
7
|
require 'interlock/interlock'
|
16
|
-
require 'interlock/
|
8
|
+
require 'interlock/lock'
|
17
9
|
require 'interlock/action_controller'
|
18
10
|
require 'interlock/action_view'
|
19
11
|
require 'interlock/active_record'
|
20
12
|
|
13
|
+
begin
|
14
|
+
require 'memcached'
|
15
|
+
rescue LoadError
|
16
|
+
end
|
17
|
+
|
21
18
|
unless ActionController::Base.perform_caching
|
22
19
|
RAILS_DEFAULT_LOGGER.warn "** interlock warning; config.perform_caching == false"
|
23
20
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
production = (ENV['PRODUCTION'] == "true")
|
3
|
+
|
4
|
+
config.cache_classes = production
|
3
5
|
config.whiny_nils = true
|
4
|
-
config.action_controller.consider_all_requests_local = !
|
6
|
+
config.action_controller.consider_all_requests_local = !production
|
5
7
|
config.action_controller.perform_caching = true
|
6
|
-
config.action_view.cache_template_extensions =
|
7
|
-
config.action_view.debug_rjs = !
|
8
|
+
config.action_view.cache_template_extensions = production
|
9
|
+
config.action_view.debug_rjs = !production
|
8
10
|
config.action_mailer.raise_delivery_errors = false
|
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
require "#{File.dirname(__FILE__)}/../test_helper"
|
3
3
|
|
4
|
-
class
|
4
|
+
class LockTest < Test::Unit::TestCase
|
5
5
|
|
6
6
|
KEY = "memcached_test"
|
7
7
|
LOCK = "lock:#{KEY}"
|
@@ -26,7 +26,7 @@ class MemcachedTest < Test::Unit::TestCase
|
|
26
26
|
def test_locked
|
27
27
|
CACHE.set LOCK, "Bogus"
|
28
28
|
|
29
|
-
assert_raises
|
29
|
+
assert_raises Interlock::LockAcquisitionError do
|
30
30
|
CACHE.lock(KEY, 30, 2) { "A" }
|
31
31
|
end
|
32
32
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: interlock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: "1.
|
4
|
+
version: "1.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ""
|
@@ -30,18 +30,10 @@ cert_chain:
|
|
30
30
|
yZ0=
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2008-
|
33
|
+
date: 2008-02-04 00:00:00 -05:00
|
34
34
|
default_executable:
|
35
|
-
dependencies:
|
36
|
-
|
37
|
-
name: memcache_client
|
38
|
-
version_requirement:
|
39
|
-
version_requirements: !ruby/object:Gem::Requirement
|
40
|
-
requirements:
|
41
|
-
- - ">="
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
version: 1.5.0
|
44
|
-
version:
|
35
|
+
dependencies: []
|
36
|
+
|
45
37
|
description: A Rails plugin for maintainable and high-efficiency caching.
|
46
38
|
email: ""
|
47
39
|
executables: []
|
@@ -60,7 +52,7 @@ files:
|
|
60
52
|
- lib/interlock/config.rb
|
61
53
|
- lib/interlock/core_extensions.rb
|
62
54
|
- lib/interlock/interlock.rb
|
63
|
-
- lib/interlock/
|
55
|
+
- lib/interlock/lock.rb
|
64
56
|
- lib/interlock.rb
|
65
57
|
- LICENSE
|
66
58
|
- Manifest
|
@@ -131,7 +123,7 @@ files:
|
|
131
123
|
- test/teardown.rb
|
132
124
|
- test/test_helper.rb
|
133
125
|
- test/unit/interlock_test.rb
|
134
|
-
- test/unit/
|
126
|
+
- test/unit/lock_test.rb
|
135
127
|
- TODO
|
136
128
|
- interlock.gemspec
|
137
129
|
has_rdoc: true
|
@@ -163,4 +155,4 @@ summary: A Rails plugin for maintainable and high-efficiency caching.
|
|
163
155
|
test_files:
|
164
156
|
- test/integration/server_test.rb
|
165
157
|
- test/unit/interlock_test.rb
|
166
|
-
- test/unit/
|
158
|
+
- test/unit/lock_test.rb
|
metadata.gz.sig
CHANGED
Binary file
|
data/lib/interlock/memcached.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
|
2
|
-
class MemCache
|
3
|
-
|
4
|
-
#
|
5
|
-
# Try to acquire a global lock from memcached for a particular key.
|
6
|
-
# If successful, yield and set the key to the return value, then release
|
7
|
-
# the lock.
|
8
|
-
#
|
9
|
-
# Based on http://rubyurl.com/Sw7 , which I partially wrote.
|
10
|
-
#
|
11
|
-
|
12
|
-
def lock(key, lock_expiry = 30, retries = 5)
|
13
|
-
retries.times do |count|
|
14
|
-
response = CACHE.add("lock:#{key}", "Locked by #{Process.pid}", lock_expiry)
|
15
|
-
if response == "STORED\r\n"
|
16
|
-
begin
|
17
|
-
value = yield(CACHE.get(key))
|
18
|
-
CACHE.set(key, value)
|
19
|
-
return value
|
20
|
-
ensure
|
21
|
-
CACHE.delete("lock:#{key}")
|
22
|
-
end
|
23
|
-
else
|
24
|
-
sleep((2**count) / 2.0)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
raise MemCacheError, "Couldn't acquire lock for #{key}"
|
28
|
-
end
|
29
|
-
|
30
|
-
end
|