interlock 1.1 → 1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|