redis-em-mutex 0.1.1 → 0.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/HISTORY.rdoc +6 -0
- data/README.rdoc +53 -4
- data/Rakefile +3 -1
- data/lib/redis/em-mutex/macro.rb +123 -0
- data/lib/redis/em-mutex/version.rb +7 -0
- data/lib/redis/em-mutex.rb +9 -4
- data/lib/redis-em-mutex.rb +7 -3
- data/redis-em-mutex.gemspec +5 -5
- data/spec/redis-em-mutex-features.rb +65 -0
- data/spec/redis-em-mutex-macros.rb +232 -0
- data/spec/redis-em-mutex-namespaces.rb +1 -1
- data/spec/redis-em-mutex-semaphores.rb +2 -2
- metadata +23 -19
data/HISTORY.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -20,6 +20,7 @@ Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
|
20
20
|
* fiber-safe
|
21
21
|
* deadlock detection (only trivial cases: locking twice the same resource from the same fiber)
|
22
22
|
* mandatory lock expiration (with refreshing)
|
23
|
+
* macro-style definitions (Mutex::Macro mixin)
|
23
24
|
|
24
25
|
== BUGS/LIMITATIONS
|
25
26
|
|
@@ -31,8 +32,8 @@ Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
|
31
32
|
== REQUIREMENTS
|
32
33
|
|
33
34
|
* ruby >= 1.9 (tested: 1.9.3-p194, 1.9.2-p320, 1.9.1-p378)
|
34
|
-
* http://github.com/redis/redis-rb
|
35
|
-
* http://rubyeventmachine.com
|
35
|
+
* http://github.com/redis/redis-rb ~> 3.0.1
|
36
|
+
* http://rubyeventmachine.com ~> 1.0.0
|
36
37
|
* (optional) http://github.com/igrigorik/em-synchrony
|
37
38
|
|
38
39
|
== INSTALL
|
@@ -41,7 +42,7 @@ Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
|
41
42
|
|
42
43
|
==== Gemfile
|
43
44
|
|
44
|
-
gem "redis-em-mutex", "~> 0.1.
|
45
|
+
gem "redis-em-mutex", "~> 0.1.2"
|
45
46
|
|
46
47
|
==== Github
|
47
48
|
|
@@ -100,7 +101,7 @@ Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
|
100
101
|
|
101
102
|
EM.synchrony do
|
102
103
|
ns.synchronize('foo') do
|
103
|
-
.... do something with foo
|
104
|
+
.... do something with foo
|
104
105
|
end
|
105
106
|
...
|
106
107
|
EM.stop
|
@@ -140,6 +141,54 @@ Author:: Rafał Michalski (mailto:rafal@yeondir.com)
|
|
140
141
|
EM.stop
|
141
142
|
end
|
142
143
|
|
144
|
+
=== Macro-style definition
|
145
|
+
|
146
|
+
Borrowed from http://github.com/kenn/redis-mutex.
|
147
|
+
Redis::EM::Mutex::Macro is a mixin which protects selected instance methods of a class with a mutex.
|
148
|
+
The locking scope will be Mutex global namespace + class name + method name.
|
149
|
+
|
150
|
+
class TheClass
|
151
|
+
include Redis::EM::Mutex::Macro
|
152
|
+
|
153
|
+
auto_mutex
|
154
|
+
def critical_run
|
155
|
+
... do some critical stuff
|
156
|
+
....only one fiber in one process on one machine is executing this instance method of any instance of defined class
|
157
|
+
end
|
158
|
+
|
159
|
+
auto_mutex expire: 100, ns: '**TheClass**'
|
160
|
+
# all critical methods defined later will inherit above options unless overridden
|
161
|
+
|
162
|
+
auto_mutex # start and stop will be protected
|
163
|
+
def start
|
164
|
+
...
|
165
|
+
end
|
166
|
+
|
167
|
+
def stop
|
168
|
+
...
|
169
|
+
end
|
170
|
+
|
171
|
+
no_auto_mutex
|
172
|
+
def some_unprotected_method
|
173
|
+
...
|
174
|
+
end
|
175
|
+
|
176
|
+
auto_mutex :run_long, expire: 100000, block: 10, on_timeout: :cant_run
|
177
|
+
def run_long
|
178
|
+
...
|
179
|
+
end
|
180
|
+
|
181
|
+
def cant_run
|
182
|
+
...
|
183
|
+
end
|
184
|
+
|
185
|
+
def foo
|
186
|
+
...
|
187
|
+
end
|
188
|
+
auto_mutex :foo, block: 0, on_timeout: proc { puts 'bar!' }
|
189
|
+
|
190
|
+
end
|
191
|
+
|
143
192
|
=== Advanced
|
144
193
|
|
145
194
|
mutex = Redis::EM::Mutex.new('resource1', 'resource2', expire: 60)
|
data/Rakefile
CHANGED
@@ -0,0 +1,123 @@
|
|
1
|
+
class Redis
|
2
|
+
module EM
|
3
|
+
class Mutex
|
4
|
+
# Macro-style definition
|
5
|
+
#
|
6
|
+
# idea and some code borrowed from http://github.com/kenn/redis-mutex and enhanced
|
7
|
+
#
|
8
|
+
# class ClassWithCriticalMethods
|
9
|
+
# include Redis::EM::Mutex::Macro
|
10
|
+
#
|
11
|
+
# auto_mutex
|
12
|
+
# def critical
|
13
|
+
# ... do some critical stuff
|
14
|
+
# ....only one fiber in one process on one machine is executing this instance method of any instance of defined class
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
module Macro
|
19
|
+
def self.included(klass)
|
20
|
+
klass.extend ClassMethods
|
21
|
+
klass.class_eval do
|
22
|
+
class << self
|
23
|
+
attr_reader :auto_mutex_methods, :auto_mutex_options
|
24
|
+
attr_accessor :auto_mutex_enabled
|
25
|
+
end
|
26
|
+
@auto_mutex_methods = {}
|
27
|
+
@auto_mutex_options = {:ns => Redis::EM::Mutex.ns ? "#{Redis::EM::Mutex.ns}:#{klass.name}" : klass.name}
|
28
|
+
@auto_mutex_enabled = false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
# auto_mutex [*method_names], [options]
|
34
|
+
#
|
35
|
+
# options are:
|
36
|
+
# - :expire - see Mutex.new
|
37
|
+
# - :block - see Mutex.new
|
38
|
+
# - :ns - custom namespace, if not present name of the class that includes Macro is used
|
39
|
+
# - :on_timeout - if defined, this proc/method will be called instead of raising MutexTimeout error
|
40
|
+
#
|
41
|
+
# If method_names are provided (names of already defined methods or defined in the future)
|
42
|
+
# they become protected with mutex.
|
43
|
+
#
|
44
|
+
# If options are provided wihout method_names, they will become default options
|
45
|
+
# for subsequent auto_mutex calls.
|
46
|
+
#
|
47
|
+
# If auto_mutex is called without arguments then any method further defined will also be protected.
|
48
|
+
# To disable implicit auto_mutex use no_auto_mutex.
|
49
|
+
def auto_mutex(*args)
|
50
|
+
options = args.last.kind_of?(Hash) ? args.pop : {}
|
51
|
+
if args.each {|target|
|
52
|
+
self.auto_mutex_methods[target] = self.auto_mutex_options.merge(options)
|
53
|
+
auto_mutex_method_added(target) if method_defined? target
|
54
|
+
}.empty?
|
55
|
+
if options.empty?
|
56
|
+
self.auto_mutex_enabled = true
|
57
|
+
else
|
58
|
+
self.auto_mutex_options.update(options)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Switch off implicit auto_mutex.
|
64
|
+
# After calling no_auto_mutex if any new method is defined it won't be protected
|
65
|
+
# unless explicitely specified with auto_mutex.
|
66
|
+
def no_auto_mutex
|
67
|
+
self.auto_mutex_enabled = false
|
68
|
+
end
|
69
|
+
|
70
|
+
def method_added(target)
|
71
|
+
return if target.to_s =~ /_(?:with|without|on_timeout)_auto_mutex$/
|
72
|
+
return unless self.auto_mutex_methods[target] || self.auto_mutex_enabled
|
73
|
+
auto_mutex_method_added(target)
|
74
|
+
end
|
75
|
+
|
76
|
+
def auto_mutex_method_added(target)
|
77
|
+
without_method = "#{target}_without_auto_mutex"
|
78
|
+
with_method = "#{target}_with_auto_mutex"
|
79
|
+
timeout_method = "#{target}_on_timeout_auto_mutex"
|
80
|
+
return if method_defined?(without_method)
|
81
|
+
|
82
|
+
options = self.auto_mutex_methods[target] || self.auto_mutex_options
|
83
|
+
mutex = nil
|
84
|
+
|
85
|
+
on_timeout = options[:on_timeout] || options[:after_failure]
|
86
|
+
|
87
|
+
if on_timeout.respond_to?(:call)
|
88
|
+
define_method(timeout_method, &on_timeout)
|
89
|
+
elsif on_timeout.is_a?(Symbol)
|
90
|
+
timeout_method = on_timeout
|
91
|
+
end
|
92
|
+
|
93
|
+
define_method(with_method) do |*args, &blk|
|
94
|
+
mutex||= Redis::EM::Mutex.new target, options
|
95
|
+
response = nil
|
96
|
+
|
97
|
+
begin
|
98
|
+
if mutex.refresh
|
99
|
+
response = __send__(without_method, *args, &blk)
|
100
|
+
else
|
101
|
+
mutex.synchronize do
|
102
|
+
response = __send__(without_method, *args, &blk)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
rescue Redis::EM::Mutex::MutexTimeout => e
|
106
|
+
if respond_to?(timeout_method)
|
107
|
+
response = __send__(timeout_method, *args, &blk)
|
108
|
+
else
|
109
|
+
raise e
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
response
|
114
|
+
end
|
115
|
+
|
116
|
+
alias_method without_method, target
|
117
|
+
alias_method target, with_method
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
data/lib/redis/em-mutex.rb
CHANGED
@@ -3,6 +3,7 @@ require 'ostruct'
|
|
3
3
|
require 'securerandom'
|
4
4
|
require 'redis/connection/synchrony' unless defined? Redis::Connection::Synchrony
|
5
5
|
require 'redis'
|
6
|
+
require 'redis/em-mutex/version'
|
6
7
|
|
7
8
|
class Redis
|
8
9
|
module EM
|
@@ -13,12 +14,15 @@ class Redis
|
|
13
14
|
# Methods of this class are NOT thread-safe.
|
14
15
|
# They are machine/process/fiber-safe.
|
15
16
|
# All method calls must be invoked only from EventMachine's reactor thread.
|
17
|
+
# Wrap mutex calls in EventMachine.shedule from non-reactor threads.
|
16
18
|
#
|
17
19
|
# - The terms "lock" and "semaphore" used in documentation are synonims.
|
18
|
-
# - The term "owner" denotes a Ruby Fiber in
|
20
|
+
# - The term "owner" denotes a Ruby Fiber executing code in the scope of Machine/Process/Fiber
|
21
|
+
# possessing exclusively a named semaphore(s).
|
19
22
|
#
|
20
23
|
class Mutex
|
21
|
-
|
24
|
+
|
25
|
+
autoload :Macro, 'redis/em-mutex/macro'
|
22
26
|
|
23
27
|
module Errors
|
24
28
|
class MutexError < RuntimeError; end
|
@@ -125,7 +129,8 @@ class Redis
|
|
125
129
|
end
|
126
130
|
end
|
127
131
|
|
128
|
-
# Returns `true` if this semaphore (all the locked `names`) is currently being held by calling fiber
|
132
|
+
# Returns `true` if this semaphore (all the locked `names`) is currently being held by calling fiber
|
133
|
+
# (if executing fiber is the owner).
|
129
134
|
def owned?
|
130
135
|
!!if @locked_id
|
131
136
|
lock_full_ident = owner_ident(@locked_id)
|
@@ -360,7 +365,7 @@ class Redis
|
|
360
365
|
}.reject {|_k, v| v.nil?})
|
361
366
|
end
|
362
367
|
if (redis = opts.redis) && !opts.url
|
363
|
-
redis_updater.call redis
|
368
|
+
redis_updater.call redis.client
|
364
369
|
elsif opts.url
|
365
370
|
redis_options[:url] = opts.url
|
366
371
|
end
|
data/lib/redis-em-mutex.rb
CHANGED
data/redis-em-mutex.gemspec
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
$:.unshift "lib"
|
2
|
-
require 'redis/em-mutex'
|
2
|
+
require 'redis/em-mutex/version'
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "redis-em-mutex"
|
@@ -18,11 +18,11 @@ Gem::Specification.new do |s|
|
|
18
18
|
"--main" << "README.rdoc"
|
19
19
|
s.has_rdoc = true
|
20
20
|
s.extra_rdoc_files = ["README.rdoc"]
|
21
|
-
s.requirements << "Redis server"
|
22
|
-
s.add_runtime_dependency "redis", ">= 3.0.
|
21
|
+
s.requirements << "Redis server 2.4+"
|
22
|
+
s.add_runtime_dependency "redis", ">= 3.0.1"
|
23
23
|
s.add_runtime_dependency "hiredis", "~> 0.4.5"
|
24
|
-
s.add_runtime_dependency "eventmachine", ">= 0.
|
24
|
+
s.add_runtime_dependency "eventmachine", ">= 1.0.0.beta.1"
|
25
25
|
s.add_development_dependency "rspec", "~> 2.8.0"
|
26
|
-
s.add_development_dependency "eventmachine", "
|
26
|
+
s.add_development_dependency "eventmachine", "~> 1.0.0"
|
27
27
|
s.add_development_dependency "em-synchrony", "~> 1.0.0"
|
28
28
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
$:.unshift "lib"
|
2
2
|
require 'em-synchrony'
|
3
|
+
require 'em-synchrony/connection_pool'
|
3
4
|
require 'redis-em-mutex'
|
4
5
|
|
5
6
|
describe Redis::EM::Mutex do
|
@@ -18,6 +19,67 @@ describe Redis::EM::Mutex do
|
|
18
19
|
}.to raise_error(described_class::MutexError, /Can not establish watcher channel connection!/)
|
19
20
|
end
|
20
21
|
|
22
|
+
it "should setup with redis connection pool" do
|
23
|
+
described_class.setup(redis: @redis_pool)
|
24
|
+
described_class.class_variable_get(:'@@redis_pool').should be @redis_pool
|
25
|
+
redis = described_class.class_variable_get(:'@@redis_watcher')
|
26
|
+
described_class.stop_watcher
|
27
|
+
redis.should be_an_instance_of Redis
|
28
|
+
redis.client.host.should eq 'localhost'
|
29
|
+
redis.client.db.should eq 1
|
30
|
+
redis.client.scheme.should eq 'redis'
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should setup with various options" do
|
34
|
+
described_class.setup do |cfg|
|
35
|
+
cfg.expire = 42 # - sets global Mutex.default_expire
|
36
|
+
cfg.ns = 'redis rulez!' # - sets global Mutex.namespace
|
37
|
+
cfg.reconnect_max = -1 # - maximum num. of attempts to re-establish
|
38
|
+
cfg.url = 'redis://127.0.0.1/2'
|
39
|
+
cfg.size = 10
|
40
|
+
end
|
41
|
+
described_class.namespace.should eq 'redis rulez!'
|
42
|
+
described_class.default_expire.should eq 42
|
43
|
+
described_class.class_variable_get(:'@@connection_retry_max').should eq -1
|
44
|
+
redis_pool = described_class.class_variable_get(:'@@redis_pool')
|
45
|
+
redis_pool.should be_an_instance_of EM::Synchrony::ConnectionPool
|
46
|
+
redis_pool.should_not be @redis_pool
|
47
|
+
redis_pool.client.host.should eq '127.0.0.1'
|
48
|
+
redis_pool.client.db.should eq 2
|
49
|
+
redis_pool.client.port.should eq 6379
|
50
|
+
redis_pool.client.scheme.should eq 'redis'
|
51
|
+
redis = described_class.class_variable_get(:'@@redis_watcher')
|
52
|
+
described_class.stop_watcher
|
53
|
+
redis.should be_an_instance_of Redis
|
54
|
+
redis.client.host.should eq '127.0.0.1'
|
55
|
+
redis.client.db.should eq 2
|
56
|
+
redis.client.port.should eq 6379
|
57
|
+
redis.client.scheme.should eq 'redis'
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should setup with separate redis options" do
|
61
|
+
described_class.setup do |cfg|
|
62
|
+
cfg.scheme = 'redis'
|
63
|
+
cfg.host = 'localhost'
|
64
|
+
cfg.port = 6379
|
65
|
+
cfg.db = 3
|
66
|
+
end
|
67
|
+
redis_pool = described_class.class_variable_get(:'@@redis_pool')
|
68
|
+
redis_pool.should be_an_instance_of EM::Synchrony::ConnectionPool
|
69
|
+
redis_pool.should_not be @redis_pool
|
70
|
+
redis_pool.client.host.should eq 'localhost'
|
71
|
+
redis_pool.client.db.should eq 3
|
72
|
+
redis_pool.client.port.should eq 6379
|
73
|
+
redis_pool.client.scheme.should eq 'redis'
|
74
|
+
redis = described_class.class_variable_get(:'@@redis_watcher')
|
75
|
+
redis.should be_an_instance_of Redis
|
76
|
+
described_class.stop_watcher
|
77
|
+
redis.client.host.should eq 'localhost'
|
78
|
+
redis.client.db.should eq 3
|
79
|
+
redis.client.port.should eq 6379
|
80
|
+
redis.client.scheme.should eq 'redis'
|
81
|
+
end
|
82
|
+
|
21
83
|
around(:each) do |testcase|
|
22
84
|
@after_em_stop = nil
|
23
85
|
::EM.synchrony do
|
@@ -30,4 +92,7 @@ describe Redis::EM::Mutex do
|
|
30
92
|
@after_em_stop.call if @after_em_stop
|
31
93
|
end
|
32
94
|
|
95
|
+
before(:all) do
|
96
|
+
@redis_pool = EM::Synchrony::ConnectionPool.new(size: 10) { Redis.new url: 'redis://localhost/1' }
|
97
|
+
end
|
33
98
|
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
$:.unshift "lib"
|
2
|
+
require 'securerandom'
|
3
|
+
require 'em-synchrony'
|
4
|
+
require 'em-synchrony/fiber_iterator'
|
5
|
+
require 'redis-em-mutex'
|
6
|
+
|
7
|
+
class Test
|
8
|
+
include Redis::EM::Mutex::Macro
|
9
|
+
auto_mutex :method5
|
10
|
+
def method1; end
|
11
|
+
auto_mutex
|
12
|
+
def method2; end
|
13
|
+
no_auto_mutex
|
14
|
+
def method3; end
|
15
|
+
def method4; end
|
16
|
+
def method5; end
|
17
|
+
auto_mutex :method4
|
18
|
+
def method6; end
|
19
|
+
auto_mutex :method6, :method7, block: 10, on_timeout: proc { }
|
20
|
+
def method7; end
|
21
|
+
|
22
|
+
auto_mutex block: 20
|
23
|
+
|
24
|
+
auto_mutex
|
25
|
+
def test(redis, key, value)
|
26
|
+
redis.set key, value
|
27
|
+
::EM::Synchrony.sleep 0.1
|
28
|
+
redis.get key
|
29
|
+
end
|
30
|
+
|
31
|
+
def recursive(n=1, &blk)
|
32
|
+
if n < 10
|
33
|
+
recursive(n+1, &blk)
|
34
|
+
else
|
35
|
+
yield n
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
no_auto_mutex
|
40
|
+
def test_no_mutex(redis, key, value)
|
41
|
+
redis.set key, value
|
42
|
+
::EM::Synchrony.sleep 0.1
|
43
|
+
redis.get key
|
44
|
+
end
|
45
|
+
|
46
|
+
auto_mutex :may_timeout, block: 0, on_timeout: proc { false }
|
47
|
+
def may_timeout; yield; end
|
48
|
+
|
49
|
+
auto_mutex :may_timeout_no_fallback, block: 0
|
50
|
+
def may_timeout_no_fallback; yield; end
|
51
|
+
|
52
|
+
auto_mutex :may_timeout_method_fallback, block: 0, on_timeout: :may_timeout_timed_out
|
53
|
+
def may_timeout_method_fallback; yield; end
|
54
|
+
def may_timeout_timed_out; false; end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
describe Redis::EM::Mutex::Macro do
|
59
|
+
|
60
|
+
it "should define auto_mutex methods" do
|
61
|
+
Test.auto_mutex_methods.keys.should eq [:method5, :method4, :method6, :method7, :may_timeout,
|
62
|
+
:may_timeout_no_fallback, :may_timeout_method_fallback]
|
63
|
+
[:method5, :method2, :method4, :test, :recursive,
|
64
|
+
:may_timeout_no_fallback, :may_timeout_method_fallback].each do |name|
|
65
|
+
Test.method_defined?(name).should be_true
|
66
|
+
Test.method_defined?("#{name}_without_auto_mutex").should be_true
|
67
|
+
Test.method_defined?("#{name}_with_auto_mutex").should be_true
|
68
|
+
Test.method_defined?("#{name}_on_timeout_auto_mutex").should be_false
|
69
|
+
end
|
70
|
+
[:method6, :method7, :may_timeout].each do |name|
|
71
|
+
Test.method_defined?(name).should be_true
|
72
|
+
Test.method_defined?("#{name}_without_auto_mutex").should be_true
|
73
|
+
Test.method_defined?("#{name}_with_auto_mutex").should be_true
|
74
|
+
Test.method_defined?("#{name}_on_timeout_auto_mutex").should be_true
|
75
|
+
end
|
76
|
+
[:method1, :method3, :test_no_mutex, :may_timeout_timed_out].each do |name|
|
77
|
+
Test.method_defined?(name).should be_true
|
78
|
+
Test.method_defined?("#{name}_without_auto_mutex").should be_false
|
79
|
+
Test.method_defined?("#{name}_with_auto_mutex").should be_false
|
80
|
+
Test.method_defined?("#{name}_on_timeout_auto_mutex").should be_false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should method run unprotected" do
|
85
|
+
iterate = 10.times.map { Test.new }
|
86
|
+
test_key = @test_key
|
87
|
+
results = {}
|
88
|
+
::EM::Synchrony::FiberIterator.new(iterate, iterate.length).each do |test|
|
89
|
+
begin
|
90
|
+
redis = Redis.new @redis_options
|
91
|
+
value = test.__id__.to_s
|
92
|
+
results[value] = test.test_no_mutex(redis, test_key, value)
|
93
|
+
rescue Exception => e
|
94
|
+
@exception = e
|
95
|
+
end
|
96
|
+
end
|
97
|
+
results.length.should eq iterate.length
|
98
|
+
results.each_pair do |k, v|
|
99
|
+
v.should eq results.values.last
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should protect auto_mutex method" do
|
104
|
+
iterate = 10.times.map { Test.new }
|
105
|
+
test_key = @test_key
|
106
|
+
results = {}
|
107
|
+
::EM::Synchrony::FiberIterator.new(iterate, iterate.length).each do |test|
|
108
|
+
begin
|
109
|
+
redis = Redis.new @redis_options
|
110
|
+
value = test.__id__.to_s
|
111
|
+
results[value] = test.test(redis, test_key, value)
|
112
|
+
rescue Exception => e
|
113
|
+
@exception = e
|
114
|
+
end
|
115
|
+
end
|
116
|
+
results.length.should eq iterate.length
|
117
|
+
results.each_pair do |k, v|
|
118
|
+
k.should eq v
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should allow recursive calls to protected methods" do
|
123
|
+
iterate = 10.times.map { Test.new }
|
124
|
+
test_key = @test_key
|
125
|
+
results = {}
|
126
|
+
::EM::Synchrony::FiberIterator.new(iterate, iterate.length).each do |test|
|
127
|
+
begin
|
128
|
+
redis = Redis.new @redis_options
|
129
|
+
value = test.__id__.to_s
|
130
|
+
results[value] = test.recursive do |n|
|
131
|
+
redis.set test_key, value
|
132
|
+
::EM::Synchrony.sleep 0.1
|
133
|
+
[redis.get(test_key), n]
|
134
|
+
end
|
135
|
+
rescue Exception => e
|
136
|
+
@exception = e
|
137
|
+
end
|
138
|
+
end
|
139
|
+
results.length.should eq iterate.length
|
140
|
+
results.each_pair do |k, v|
|
141
|
+
v.should eq [k, 10]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should call on_timout lambda when locking times out" do
|
146
|
+
::EM::Synchrony.next_tick do
|
147
|
+
begin
|
148
|
+
Test.new.may_timeout do
|
149
|
+
::EM::Synchrony.sleep 0.2
|
150
|
+
true
|
151
|
+
end.should be_true
|
152
|
+
rescue Exception => e
|
153
|
+
@exception = e
|
154
|
+
end
|
155
|
+
end
|
156
|
+
::EM::Synchrony.sleep 0.1
|
157
|
+
Test.new.may_timeout do
|
158
|
+
true
|
159
|
+
end.should be_false
|
160
|
+
::EM::Synchrony.sleep 0.15
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should raise MutexTimeout when locking times out" do
|
164
|
+
::EM::Synchrony.next_tick do
|
165
|
+
begin
|
166
|
+
Test.new.may_timeout_no_fallback do
|
167
|
+
::EM::Synchrony.sleep 0.2
|
168
|
+
true
|
169
|
+
end.should be_true
|
170
|
+
rescue Exception => e
|
171
|
+
@exception = e
|
172
|
+
end
|
173
|
+
end
|
174
|
+
::EM::Synchrony.sleep 0.1
|
175
|
+
expect {
|
176
|
+
Test.new.may_timeout_no_fallback do
|
177
|
+
true
|
178
|
+
end
|
179
|
+
}.to raise_error(Redis::EM::Mutex::MutexTimeout)
|
180
|
+
::EM::Synchrony.sleep 0.15
|
181
|
+
end
|
182
|
+
|
183
|
+
it "should call on_timout method when locking times out" do
|
184
|
+
::EM::Synchrony.next_tick do
|
185
|
+
begin
|
186
|
+
Test.new.may_timeout_method_fallback do
|
187
|
+
::EM::Synchrony.sleep 0.2
|
188
|
+
true
|
189
|
+
end.should be_true
|
190
|
+
rescue Exception => e
|
191
|
+
@exception = e
|
192
|
+
end
|
193
|
+
end
|
194
|
+
::EM::Synchrony.sleep 0.1
|
195
|
+
Test.new.may_timeout_method_fallback do
|
196
|
+
true
|
197
|
+
end.should be_false
|
198
|
+
::EM::Synchrony.sleep 0.15
|
199
|
+
end
|
200
|
+
|
201
|
+
around(:each) do |testcase|
|
202
|
+
@after_em_stop = nil
|
203
|
+
@exception = nil
|
204
|
+
::EM.synchrony do
|
205
|
+
begin
|
206
|
+
testcase.call
|
207
|
+
raise @exception if @exception
|
208
|
+
Redis::EM::Mutex.stop_watcher(false)
|
209
|
+
rescue => e
|
210
|
+
Redis::EM::Mutex.stop_watcher(true)
|
211
|
+
raise e
|
212
|
+
ensure
|
213
|
+
::EM.stop
|
214
|
+
end
|
215
|
+
end
|
216
|
+
@after_em_stop.call if @after_em_stop
|
217
|
+
end
|
218
|
+
|
219
|
+
before(:all) do
|
220
|
+
@redis_options = {:driver => :synchrony}
|
221
|
+
@test_key = SecureRandom.base64(24)
|
222
|
+
Redis::EM::Mutex.setup @redis_options.merge(size: 11, ns: SecureRandom.base64(15))
|
223
|
+
end
|
224
|
+
|
225
|
+
after(:all) do
|
226
|
+
::EM.synchrony do
|
227
|
+
Redis.new(@redis_options).del @test_key
|
228
|
+
EM.stop
|
229
|
+
end
|
230
|
+
# @lock_names
|
231
|
+
end
|
232
|
+
end
|
@@ -323,7 +323,7 @@ describe Redis::EM::Mutex do
|
|
323
323
|
begin
|
324
324
|
testcase.call
|
325
325
|
raise @exception if @exception
|
326
|
-
described_class.stop_watcher
|
326
|
+
described_class.stop_watcher
|
327
327
|
rescue => e
|
328
328
|
described_class.stop_watcher(true)
|
329
329
|
raise e
|
@@ -335,7 +335,7 @@ describe Redis::EM::Mutex do
|
|
335
335
|
end
|
336
336
|
|
337
337
|
before(:all) do
|
338
|
-
@redis_options = {}
|
338
|
+
@redis_options = {:driver => :synchrony}
|
339
339
|
described_class.setup @redis_options.merge(size: 11)
|
340
340
|
@lock_names = 10.times.map {
|
341
341
|
SecureRandom.random_bytes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redis-em-mutex
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,22 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-13 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: redis
|
16
|
-
requirement: &
|
16
|
+
requirement: &90657860 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version: 3.0.
|
21
|
+
version: 3.0.1
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *90657860
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: hiredis
|
27
|
-
requirement: &
|
27
|
+
requirement: &90657060 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,21 +32,21 @@ dependencies:
|
|
32
32
|
version: 0.4.5
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *90657060
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: eventmachine
|
38
|
-
requirement: &
|
38
|
+
requirement: &90656280 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ! '>='
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.
|
43
|
+
version: 1.0.0.beta.1
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *90656280
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rspec
|
49
|
-
requirement: &
|
49
|
+
requirement: &90655560 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,21 +54,21 @@ dependencies:
|
|
54
54
|
version: 2.8.0
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
57
|
+
version_requirements: *90655560
|
58
58
|
- !ruby/object:Gem::Dependency
|
59
59
|
name: eventmachine
|
60
|
-
requirement: &
|
60
|
+
requirement: &90654360 !ruby/object:Gem::Requirement
|
61
61
|
none: false
|
62
62
|
requirements:
|
63
|
-
- -
|
63
|
+
- - ~>
|
64
64
|
- !ruby/object:Gem::Version
|
65
|
-
version: 1.0.0
|
65
|
+
version: 1.0.0
|
66
66
|
type: :development
|
67
67
|
prerelease: false
|
68
|
-
version_requirements: *
|
68
|
+
version_requirements: *90654360
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: em-synchrony
|
71
|
-
requirement: &
|
71
|
+
requirement: &90652800 !ruby/object:Gem::Requirement
|
72
72
|
none: false
|
73
73
|
requirements:
|
74
74
|
- - ~>
|
@@ -76,7 +76,7 @@ dependencies:
|
|
76
76
|
version: 1.0.0
|
77
77
|
type: :development
|
78
78
|
prerelease: false
|
79
|
-
version_requirements: *
|
79
|
+
version_requirements: *90652800
|
80
80
|
description: Cross server-process-fiber EventMachine + Redis based semaphore with
|
81
81
|
many features
|
82
82
|
email: rafal@yeondir.com
|
@@ -90,8 +90,11 @@ files:
|
|
90
90
|
- Rakefile
|
91
91
|
- lib/redis-em-mutex.rb
|
92
92
|
- lib/redis/em-mutex.rb
|
93
|
+
- lib/redis/em-mutex/macro.rb
|
94
|
+
- lib/redis/em-mutex/version.rb
|
93
95
|
- redis-em-mutex.gemspec
|
94
96
|
- spec/redis-em-mutex-features.rb
|
97
|
+
- spec/redis-em-mutex-macros.rb
|
95
98
|
- spec/redis-em-mutex-namespaces.rb
|
96
99
|
- spec/redis-em-mutex-semaphores.rb
|
97
100
|
homepage: http://github.com/royaltm/redis-em-mutex
|
@@ -117,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
120
|
- !ruby/object:Gem::Version
|
118
121
|
version: '0'
|
119
122
|
requirements:
|
120
|
-
- Redis server
|
123
|
+
- Redis server 2.4+
|
121
124
|
rubyforge_project:
|
122
125
|
rubygems_version: 1.8.17
|
123
126
|
signing_key:
|
@@ -127,3 +130,4 @@ test_files:
|
|
127
130
|
- spec/redis-em-mutex-namespaces.rb
|
128
131
|
- spec/redis-em-mutex-semaphores.rb
|
129
132
|
- spec/redis-em-mutex-features.rb
|
133
|
+
- spec/redis-em-mutex-macros.rb
|