mega_mutex 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +14 -3
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/mega_mutex/{cross_process_mutex.rb → distributed_mutex.rb} +10 -4
- data/lib/mega_mutex.rb +8 -7
- data/mega_mutex.gemspec +7 -7
- data/spec/lib/mega_mutex_spec.rb +7 -7
- metadata +6 -6
data/README.markdown
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# mega_mutex
|
2
2
|
|
3
|
+
A distributed mutex for Ruby.
|
4
|
+
|
3
5
|
## Why
|
4
6
|
|
5
7
|
Sometimes I need to do this:
|
@@ -8,7 +10,7 @@ Sometimes I need to do this:
|
|
8
10
|
make_more_things
|
9
11
|
end
|
10
12
|
|
11
|
-
|
13
|
+
If I'm running several processes in parallel, I can get a race condition that means two of the processes both think there are not enough things. So we go and make some more, even though we don't need to.
|
12
14
|
|
13
15
|
## How
|
14
16
|
|
@@ -18,7 +20,7 @@ Suppose you have a ThingMaker:
|
|
18
20
|
include MegaMutex
|
19
21
|
|
20
22
|
def ensure_just_enough_things
|
21
|
-
|
23
|
+
with_distributed_mutex("ThingMaker Mutex ID") do
|
22
24
|
unless enough_things?
|
23
25
|
make_more_things
|
24
26
|
end
|
@@ -28,7 +30,12 @@ Suppose you have a ThingMaker:
|
|
28
30
|
|
29
31
|
Now, thanks to the magic of MegaMutex, you can be sure that all processes trying to run this code will wait their turn, so each one will have the chance to make exactly the right number of things, without anyone else poking their nose in.
|
30
32
|
|
31
|
-
##
|
33
|
+
## Install
|
34
|
+
|
35
|
+
sudo gem install mega_mutex
|
36
|
+
|
37
|
+
|
38
|
+
## Configure
|
32
39
|
|
33
40
|
MegaMutex uses [memcache-client](http://seattlerb.rubyforge.org/memcache-client/) to store the mutex, so your infrastructure must be set up to use memcache servers.
|
34
41
|
|
@@ -38,6 +45,10 @@ By default, MegaMutex will attempt to connect to a memcache on the local machine
|
|
38
45
|
config.memcache_servers = ['mc1', 'mc2']
|
39
46
|
end
|
40
47
|
|
48
|
+
## Help
|
49
|
+
|
50
|
+
MegaMutex was built by the [Songkick.com](http://www.songkick.com) development team. Come chat to us on [#songkick](irc://chat.freenode.net/#songkick) on freenode.net.
|
51
|
+
|
41
52
|
## Copyright
|
42
53
|
|
43
54
|
Copyright (c) 2009 Songkick.com. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -5,8 +5,8 @@ begin
|
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
7
|
gem.name = "mega_mutex"
|
8
|
-
gem.summary = %Q{
|
9
|
-
gem.description = %Q{
|
8
|
+
gem.summary = %Q{Distributed mutex for Ruby}
|
9
|
+
gem.description = %Q{Distributed mutex for Ruby}
|
10
10
|
gem.email = "developers@songkick.com"
|
11
11
|
gem.homepage = "http://github.com/songkick/mega_mutex"
|
12
12
|
gem.authors = ["Matt Johnson", "Matt Wynne"]
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.1
|
@@ -4,7 +4,13 @@ require 'memcache'
|
|
4
4
|
module MegaMutex
|
5
5
|
class TimeoutError < Exception; end
|
6
6
|
|
7
|
-
class
|
7
|
+
class DistributedMutex
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def cache
|
11
|
+
@cache ||= MemCache.new MegaMutex.configuration.memcache_servers, :namespace => MegaMutex.configuration.namespace
|
12
|
+
end
|
13
|
+
end
|
8
14
|
|
9
15
|
def initialize(key, timeout = nil)
|
10
16
|
@key = key
|
@@ -17,7 +23,7 @@ module MegaMutex
|
|
17
23
|
|
18
24
|
def run(&block)
|
19
25
|
@start_time = Time.now
|
20
|
-
log "Attempting to lock
|
26
|
+
log "Attempting to lock mutex..."
|
21
27
|
lock!
|
22
28
|
log "Locked. Running critical section..."
|
23
29
|
yield
|
@@ -76,7 +82,7 @@ module MegaMutex
|
|
76
82
|
end
|
77
83
|
|
78
84
|
def cache
|
79
|
-
|
85
|
+
self.class.cache
|
80
86
|
end
|
81
87
|
end
|
82
|
-
end
|
88
|
+
end
|
data/lib/mega_mutex.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
$:.push File.expand_path(File.dirname(__FILE__)) unless $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
-
require 'mega_mutex/
|
3
|
+
require 'mega_mutex/distributed_mutex'
|
4
4
|
|
5
5
|
# == Why
|
6
6
|
#
|
@@ -10,7 +10,7 @@ require 'mega_mutex/cross_process_mutex'
|
|
10
10
|
# make_more_things
|
11
11
|
# end
|
12
12
|
#
|
13
|
-
#
|
13
|
+
# If I'm running several processes in parallel, I can get a race condition that means two of the processes both think there are not enough things. So we go and make some more, even though we don't need to.
|
14
14
|
#
|
15
15
|
# == How
|
16
16
|
#
|
@@ -42,7 +42,7 @@ require 'mega_mutex/cross_process_mutex'
|
|
42
42
|
module MegaMutex
|
43
43
|
|
44
44
|
def self.get_current_lock(mutex_id)
|
45
|
-
|
45
|
+
DistributedMutex.new(mutex_id).current_lock
|
46
46
|
end
|
47
47
|
|
48
48
|
##
|
@@ -53,22 +53,23 @@ module MegaMutex
|
|
53
53
|
# You can optionally specify a :timeout to control how long to wait for the lock to be released
|
54
54
|
# before raising a MegaMutex::TimeoutError
|
55
55
|
#
|
56
|
-
#
|
56
|
+
# with_distributed_mutex('my_mutex_id_1234', :timeout => 20) do
|
57
57
|
# do_something!
|
58
58
|
# end
|
59
|
-
def
|
60
|
-
mutex =
|
59
|
+
def with_distributed_mutex(mutex_id, options = {}, &block)
|
60
|
+
mutex = DistributedMutex.new(mutex_id, options[:timeout])
|
61
61
|
begin
|
62
62
|
mutex.run(&block)
|
63
63
|
rescue Object => e
|
64
64
|
mega_mutex_insert_into_backtrace(
|
65
65
|
e,
|
66
|
-
/mega_mutex\.rb.*
|
66
|
+
/mega_mutex\.rb.*with_(distributed|cross_process)_mutex/,
|
67
67
|
"MegaMutex lock #{mutex_id}"
|
68
68
|
)
|
69
69
|
raise e
|
70
70
|
end
|
71
71
|
end
|
72
|
+
alias :with_cross_process_mutex :with_distributed_mutex
|
72
73
|
|
73
74
|
# inserts a line into a backtrace at the correct location
|
74
75
|
def mega_mutex_insert_into_backtrace(exception, re, newline)
|
data/mega_mutex.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{mega_mutex}
|
8
|
-
s.version = "0.2.
|
8
|
+
s.version = "0.2.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matt Johnson", "Matt Wynne"]
|
12
|
-
s.date = %q{
|
13
|
-
s.description = %q{
|
12
|
+
s.date = %q{2010-02-07}
|
13
|
+
s.description = %q{Distributed mutex for Ruby}
|
14
14
|
s.email = %q{developers@songkick.com}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
|
|
24
24
|
"Rakefile",
|
25
25
|
"VERSION",
|
26
26
|
"lib/mega_mutex.rb",
|
27
|
-
"lib/mega_mutex/
|
27
|
+
"lib/mega_mutex/distributed_mutex.rb",
|
28
28
|
"mega_mutex.gemspec",
|
29
29
|
"spec/lib/mega_mutex_spec.rb",
|
30
30
|
"spec/spec_helper.rb"
|
@@ -33,10 +33,10 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.rdoc_options = ["--charset=UTF-8"]
|
34
34
|
s.require_paths = ["lib"]
|
35
35
|
s.rubygems_version = %q{1.3.5}
|
36
|
-
s.summary = %q{
|
36
|
+
s.summary = %q{Distributed mutex for Ruby}
|
37
37
|
s.test_files = [
|
38
|
-
"spec/
|
39
|
-
"spec/
|
38
|
+
"spec/lib/mega_mutex_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
40
|
]
|
41
41
|
|
42
42
|
if s.respond_to? :specification_version then
|
data/spec/lib/mega_mutex_spec.rb
CHANGED
@@ -43,7 +43,7 @@ module MegaMutex
|
|
43
43
|
describe "when #{n} blocks try to run at the same instant in the same process" do
|
44
44
|
it "should run each in turn" do
|
45
45
|
n.times do
|
46
|
-
threads << Thread.new{
|
46
|
+
threads << Thread.new{ with_distributed_mutex(mutex_id, &@mutually_exclusive_block) }
|
47
47
|
end
|
48
48
|
wait_for_threads_to_finish
|
49
49
|
@errors.should be_empty
|
@@ -53,14 +53,14 @@ module MegaMutex
|
|
53
53
|
|
54
54
|
describe "when the first block raises an exception" do
|
55
55
|
before(:each) do
|
56
|
-
|
56
|
+
with_distributed_mutex(mutex_id) do
|
57
57
|
raise "Something went wrong in my code"
|
58
58
|
end rescue nil
|
59
59
|
end
|
60
60
|
|
61
61
|
it "the second block should find that the lock is clear and it can run" do
|
62
62
|
@success = nil
|
63
|
-
|
63
|
+
with_distributed_mutex(mutex_id) do
|
64
64
|
@success = true
|
65
65
|
end
|
66
66
|
@success.should be_true
|
@@ -86,8 +86,8 @@ module MegaMutex
|
|
86
86
|
|
87
87
|
it "should run each in turn" do
|
88
88
|
pids = []
|
89
|
-
pids << fork {
|
90
|
-
pids << fork {
|
89
|
+
pids << fork { with_distributed_mutex(mutex_id, &@mutually_exclusive_block); Kernel.exit! }
|
90
|
+
pids << fork { with_distributed_mutex(mutex_id, &@mutually_exclusive_block); Kernel.exit! }
|
91
91
|
pids.each{ |p| Process.wait(p) }
|
92
92
|
if File.exists?(@errors_file)
|
93
93
|
raise "Expected no errors but found #{File.read(@errors_file)}"
|
@@ -105,7 +105,7 @@ module MegaMutex
|
|
105
105
|
@exception = nil
|
106
106
|
@first_thread_has_started = false
|
107
107
|
threads << Thread.new do
|
108
|
-
|
108
|
+
with_distributed_mutex('foo') do
|
109
109
|
@first_thread_has_started = true
|
110
110
|
sleep 0.2
|
111
111
|
end
|
@@ -113,7 +113,7 @@ module MegaMutex
|
|
113
113
|
threads << Thread.new do
|
114
114
|
sleep 0.1 until @first_thread_has_started
|
115
115
|
begin
|
116
|
-
|
116
|
+
with_distributed_mutex('foo', :timeout => 0.1 ) do
|
117
117
|
raise 'this code should never run'
|
118
118
|
end
|
119
119
|
rescue Exception => @exception
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mega_mutex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Johnson
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date:
|
13
|
+
date: 2010-02-07 00:00:00 +00:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -33,7 +33,7 @@ dependencies:
|
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: 1.1.4
|
35
35
|
version:
|
36
|
-
description:
|
36
|
+
description: Distributed mutex for Ruby
|
37
37
|
email: developers@songkick.com
|
38
38
|
executables: []
|
39
39
|
|
@@ -50,7 +50,7 @@ files:
|
|
50
50
|
- Rakefile
|
51
51
|
- VERSION
|
52
52
|
- lib/mega_mutex.rb
|
53
|
-
- lib/mega_mutex/
|
53
|
+
- lib/mega_mutex/distributed_mutex.rb
|
54
54
|
- mega_mutex.gemspec
|
55
55
|
- spec/lib/mega_mutex_spec.rb
|
56
56
|
- spec/spec_helper.rb
|
@@ -81,7 +81,7 @@ rubyforge_project:
|
|
81
81
|
rubygems_version: 1.3.5
|
82
82
|
signing_key:
|
83
83
|
specification_version: 3
|
84
|
-
summary:
|
84
|
+
summary: Distributed mutex for Ruby
|
85
85
|
test_files:
|
86
|
-
- spec/spec_helper.rb
|
87
86
|
- spec/lib/mega_mutex_spec.rb
|
87
|
+
- spec/spec_helper.rb
|