redis-em-mutex 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/BENCHMARK.md +134 -0
- data/{HISTORY.rdoc → HISTORY.md} +10 -1
- data/README.md +89 -5
- data/Rakefile +23 -4
- data/benchmark_mutex.rb +99 -0
- data/lib/redis/em-connection-pool.rb +88 -0
- data/lib/redis/em-mutex/ns.rb +1 -1
- data/lib/redis/em-mutex/pure_handler.rb +245 -0
- data/lib/redis/em-mutex/script_handler.rb +414 -0
- data/lib/redis/em-mutex/version.rb +1 -1
- data/lib/redis/em-mutex.rb +79 -248
- data/redis-em-mutex.gemspec +2 -2
- data/spec/redis-em-mutex-condition.rb +3 -3
- data/spec/redis-em-mutex-features.rb +67 -5
- data/spec/redis-em-mutex-semaphores.rb +9 -4
- metadata +11 -5
data/BENCHMARK.md
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
BENCHMARK
|
2
|
+
=========
|
3
|
+
|
4
|
+
To measure the performance of {Redis::EM::Mutex} I've wrote a simple script called `benchmark_mutex.rb`.
|
5
|
+
The script is included in respository.
|
6
|
+
|
7
|
+
Below are the results of running tests against the following versions:
|
8
|
+
|
9
|
+
- redis-em-mutex v0.1.2
|
10
|
+
- redis-em-mutex v0.2.3
|
11
|
+
- redis-em-mutex v0.3.0 - "pure" handler
|
12
|
+
- redis-em-mutex v0.3.0 - "script" handler
|
13
|
+
|
14
|
+
To run theese tests type:
|
15
|
+
|
16
|
+
```sh
|
17
|
+
cp benchmark_mutex.rb /tmp/
|
18
|
+
|
19
|
+
git reset --hard v0.1.2
|
20
|
+
ruby /tmp/benchmark_mutex.rb
|
21
|
+
|
22
|
+
git reset --hard v0.2.3
|
23
|
+
ruby /tmp/benchmark_mutex.rb
|
24
|
+
|
25
|
+
git reset --hard v0.3.0
|
26
|
+
REDIS_EM_MUTEX_HANDLER=pure ruby benchmark_mutex.rb
|
27
|
+
REDIS_EM_MUTEX_HANDLER=script ruby benchmark_mutex.rb
|
28
|
+
```
|
29
|
+
|
30
|
+
Here are the results of running those tests on Quad Core Xeon machine
|
31
|
+
with redis-server 2.6.9 and ruby 1.9.3p374 (2013-01-15 revision 38858) [x86_64-linux].
|
32
|
+
|
33
|
+
The results may vary.
|
34
|
+
|
35
|
+
Test 1
|
36
|
+
======
|
37
|
+
|
38
|
+
Lock/unlock 1000 times using 10 concurrent fibers.
|
39
|
+
|
40
|
+
```
|
41
|
+
Version: 0.1.2, handler: N/A
|
42
|
+
lock/unlock 1000 times with concurrency: 10
|
43
|
+
user system total real
|
44
|
+
keys: 1 0.590000 0.220000 0.810000 ( 1.124918)
|
45
|
+
keys: 2 0.640000 0.220000 0.860000 ( 1.214577)
|
46
|
+
keys: 3 0.660000 0.200000 0.860000 ( 1.245093)
|
47
|
+
keys: 5 0.770000 0.140000 0.910000 ( 1.322475)
|
48
|
+
keys:10 0.890000 0.210000 1.100000 ( 1.456293)
|
49
|
+
|
50
|
+
Version: 0.2.3, handler: N/A
|
51
|
+
lock/unlock 1000 times with concurrency: 10
|
52
|
+
user system total real
|
53
|
+
keys: 1 0.640000 0.200000 0.840000 ( 1.105686)
|
54
|
+
keys: 2 1.090000 0.420000 1.510000 ( 2.014079)
|
55
|
+
keys: 3 1.450000 0.500000 1.950000 ( 2.738510)
|
56
|
+
keys: 5 1.940000 0.820000 2.760000 ( 4.136856)
|
57
|
+
keys:10 3.360000 1.900000 5.260000 ( 8.232977)
|
58
|
+
|
59
|
+
Version: 0.3.0, handler: Redis::EM::Mutex::PureHandlerMixin
|
60
|
+
lock/unlock 1000 times with concurrency: 10
|
61
|
+
user system total real
|
62
|
+
keys: 1 0.610000 0.230000 0.840000 ( 0.864242)
|
63
|
+
keys: 2 0.640000 0.220000 0.860000 ( 0.914122)
|
64
|
+
keys: 3 0.660000 0.260000 0.920000 ( 0.947300)
|
65
|
+
keys: 5 0.730000 0.250000 0.980000 ( 1.007862)
|
66
|
+
keys:10 0.840000 0.230000 1.070000 ( 1.315885)
|
67
|
+
|
68
|
+
Version: 0.3.0, handler: Redis::EM::Mutex::ScriptHandlerMixin
|
69
|
+
lock/unlock 1000 times with concurrency: 10
|
70
|
+
user system total real
|
71
|
+
keys: 1 0.290000 0.110000 0.400000 ( 0.633668)
|
72
|
+
keys: 2 0.280000 0.150000 0.430000 ( 0.714378)
|
73
|
+
keys: 3 0.290000 0.100000 0.390000 ( 0.657861)
|
74
|
+
keys: 5 0.430000 0.100000 0.530000 ( 0.775208)
|
75
|
+
keys:10 0.330000 0.150000 0.480000 ( 0.904942)
|
76
|
+
```
|
77
|
+
|
78
|
+
Test 2
|
79
|
+
======
|
80
|
+
|
81
|
+
run 100 fibers which will repeat the following actions:
|
82
|
+
|
83
|
+
- sleep some predefined time
|
84
|
+
- lock
|
85
|
+
- write some value to redis key
|
86
|
+
- increase value in redis key
|
87
|
+
- read value from that key
|
88
|
+
- delete that key
|
89
|
+
- unlock
|
90
|
+
|
91
|
+
Wait 5 seconds and then tell all the fibers to quit.
|
92
|
+
The wall time also includes the "cooling time" which lasted
|
93
|
+
after 5 seconds has elapsed to the moment where all the fibers
|
94
|
+
have ceased processing.
|
95
|
+
The resulting value is how many times the above actions were repeated
|
96
|
+
during that period.
|
97
|
+
|
98
|
+
```
|
99
|
+
Version: 0.1.2, handler: N/A
|
100
|
+
lock/write/incr/read/del/unlock in 5 seconds + cooldown period:
|
101
|
+
result user system total real
|
102
|
+
keys: 1 3256 2.290000 1.230000 3.520000 ( 5.135926)
|
103
|
+
keys: 2 3179 2.260000 1.000000 3.260000 ( 5.124043)
|
104
|
+
keys: 3 3072 2.160000 1.170000 3.330000 ( 5.128032)
|
105
|
+
keys: 5 2859 2.280000 1.070000 3.350000 ( 5.132027)
|
106
|
+
keys:10 2564 2.460000 0.860000 3.320000 ( 5.151968)
|
107
|
+
|
108
|
+
Version: 0.2.3, handler: N/A
|
109
|
+
lock/write/incr/read/del/unlock in 5 seconds + cooldown period:
|
110
|
+
result user system total real
|
111
|
+
keys: 1 3274 2.480000 1.010000 3.490000 ( 5.111753)
|
112
|
+
keys: 2 2429 2.740000 1.270000 4.010000 ( 5.204065)
|
113
|
+
keys: 3 1855 2.380000 1.210000 3.590000 ( 5.256041)
|
114
|
+
keys: 5 1309 2.890000 1.250000 4.140000 ( 5.376043)
|
115
|
+
keys:10 710 3.120000 1.190000 4.310000 ( 5.763981)
|
116
|
+
|
117
|
+
Version: 0.3.0, handler: Redis::EM::Mutex::PureHandlerMixin
|
118
|
+
lock/write/incr/read/del/unlock in 5 seconds + cooldown period:
|
119
|
+
result user system total real
|
120
|
+
keys: 1 3795 2.490000 1.400000 3.890000 ( 5.108474)
|
121
|
+
keys: 2 3788 2.600000 1.400000 4.000000 ( 5.108037)
|
122
|
+
keys: 3 3921 2.800000 1.170000 3.970000 ( 5.120059)
|
123
|
+
keys: 5 3641 2.820000 1.140000 3.960000 ( 5.112036)
|
124
|
+
keys:10 2661 2.860000 1.130000 3.990000 ( 5.152105)
|
125
|
+
|
126
|
+
Version: 0.3.0, handler: Redis::EM::Mutex::ScriptHandlerMixin
|
127
|
+
lock/write/incr/read/del/unlock in 5 seconds + cooldown period:
|
128
|
+
result user system total real
|
129
|
+
keys: 1 5177 1.980000 1.020000 3.000000 ( 5.079791)
|
130
|
+
keys: 2 5460 1.600000 1.030000 2.630000 ( 5.080049)
|
131
|
+
keys: 3 5322 1.560000 1.000000 2.560000 ( 5.088010)
|
132
|
+
keys: 5 4685 1.620000 0.810000 2.430000 ( 5.084035)
|
133
|
+
keys:10 4347 1.600000 0.770000 2.370000 ( 5.111976)
|
134
|
+
```
|
data/{HISTORY.rdoc → HISTORY.md}
RENAMED
@@ -1,5 +1,14 @@
|
|
1
|
+
0.3.0
|
2
|
+
- fixed: optimized pure handler
|
3
|
+
- added redis/em-connection-pool no more em-synchrony/connection_pool dependency
|
4
|
+
- added redis gem dependency updated to 3.0.2
|
5
|
+
- added owner_ident specs
|
6
|
+
- added redis-lua script handler
|
7
|
+
- added #can_refresh_expired? handler feature detection
|
8
|
+
- added handlers
|
9
|
+
|
1
10
|
0.2.3
|
2
|
-
- fixed: rare
|
11
|
+
- fixed: rare unlock! race condition introduced in 0.2.1
|
3
12
|
manifesting as deadlock exception when no deadlock should occur
|
4
13
|
|
5
14
|
0.2.2
|
data/README.md
CHANGED
@@ -17,9 +17,9 @@ FEATURES
|
|
17
17
|
* no CPU-intensive sleep/polling while waiting for lock to become available
|
18
18
|
* fibers waiting for the lock are signalled via Redis channel as soon as the lock
|
19
19
|
has been released (~< 1 ms)
|
20
|
+
* alternative fast "script" handler (server-side LUA script based - redis-server 2.6.x)
|
20
21
|
* multi-locks (all-or-nothing) locking (to prevent possible deadlocks when
|
21
22
|
multiple semaphores are required to be locked at once)
|
22
|
-
* best served with EM-Synchrony (uses EM::Synchrony::ConnectionPool internally)
|
23
23
|
* fiber-safe
|
24
24
|
* deadlock detection (only trivial cases: locking twice the same resource from the same owner)
|
25
25
|
* mandatory lock expiration (with refreshing)
|
@@ -39,7 +39,7 @@ REQUIREMENTS
|
|
39
39
|
------------
|
40
40
|
|
41
41
|
* ruby >= 1.9 (tested: ruby 1.9.3p374, 1.9.3-p194, 1.9.2-p320, 1.9.1-p378)
|
42
|
-
* http://github.com/redis/redis-rb ~> 3.0.
|
42
|
+
* http://github.com/redis/redis-rb ~> 3.0.2
|
43
43
|
* http://rubyeventmachine.com ~> 1.0.0
|
44
44
|
* (optional) http://github.com/igrigorik/em-synchrony
|
45
45
|
|
@@ -53,7 +53,7 @@ $ [sudo] gem install redis-em-mutex
|
|
53
53
|
#### Gemfile
|
54
54
|
|
55
55
|
```ruby
|
56
|
-
gem "redis-em-mutex", "~> 0.
|
56
|
+
gem "redis-em-mutex", "~> 0.3.0"
|
57
57
|
```
|
58
58
|
|
59
59
|
#### Github
|
@@ -62,6 +62,27 @@ gem "redis-em-mutex", "~> 0.2.3"
|
|
62
62
|
git clone git://github.com/royaltm/redis-em-mutex.git
|
63
63
|
```
|
64
64
|
|
65
|
+
UPGRADING
|
66
|
+
---------
|
67
|
+
|
68
|
+
0.2.x -> 0.3.x
|
69
|
+
|
70
|
+
To upgrade redis-em-mutex on production from 0.2.x to 0.3.x you must make sure the correct handler has been
|
71
|
+
selected. See more on HANDLERS below.
|
72
|
+
|
73
|
+
The "pure" and "script" handlers are not compatible. Two different handlers must not utilize the same semaphore-key space.
|
74
|
+
|
75
|
+
Because only the "pure" handler is compatible with redis-em-mutex <= 0.2.x, when upgrading live production make sure to add
|
76
|
+
`handler: :pure` option to `Redis::EM::Mutex.setup` or set the environment variable on production app servers:
|
77
|
+
|
78
|
+
```sh
|
79
|
+
REDIS_EM_MUTEX_HANDLER=pure
|
80
|
+
export REDIS_EM_MUTEX_HANDLER
|
81
|
+
```
|
82
|
+
Upgrading from "pure" to "script" handler requires that all "pure" handler locks __MUST BE DELETED__ from redis-server beforehand.
|
83
|
+
Neglecting that will result in possible deadlocks. The "script" handler assumes that the lock expiration process is handled
|
84
|
+
by redis-server's PEXPIREAT feature. The "pure" handler does not set timeouts on keys. It handles expiration differently.
|
85
|
+
|
65
86
|
USAGE
|
66
87
|
-----
|
67
88
|
|
@@ -107,6 +128,58 @@ USAGE
|
|
107
128
|
end
|
108
129
|
```
|
109
130
|
|
131
|
+
### Handlers
|
132
|
+
|
133
|
+
There are 2 different mutex implementations since version 0.3.0.
|
134
|
+
|
135
|
+
* The "pure" classic handler utilizes redis optimistic transaction commands (watch/multi).
|
136
|
+
This handler works with redis-server 2.4.x and later.
|
137
|
+
* The new "script" handler takes advantage of fast atomic server side operations written in LUA.
|
138
|
+
Therefore the "script" handler is compatible only with redis-server 2.6.x and later.
|
139
|
+
|
140
|
+
__IMPORTANT__: The "pure" and "script" implementations are not compatible. The values that each handler stores in semaphore keys have different meaning to them.
|
141
|
+
You can not operate on the same set of keys using both handlers from e.g. different applications or application versions.
|
142
|
+
See UPGRADING for more info on this.
|
143
|
+
|
144
|
+
You choose your preferred implementation with `handler` option:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
Redis::EM::Mutex.setup(handler: :script)
|
148
|
+
Redis::EM::Mutex.handler # "Redis::EM::Mutex::ScriptHandlerMixin"
|
149
|
+
|
150
|
+
# or
|
151
|
+
|
152
|
+
Redis::EM::Mutex.setup do |opts|
|
153
|
+
opts.handler = :pure
|
154
|
+
end
|
155
|
+
Redis::EM::Mutex.handler # "Redis::EM::Mutex::PureHandlerMixin"
|
156
|
+
```
|
157
|
+
|
158
|
+
You may also setup `REDIS_EM_MUTEX_HANDLER` environment variable to preferred implementation name.
|
159
|
+
Passing `handler` option to {Redis::EM::Mutex.setup} overrides environment variable.
|
160
|
+
|
161
|
+
The default handler option is `auto` which selects best handler available for your redis-server.
|
162
|
+
It's good for quick sandbox setup, however you should set explicitly which handler you require on production.
|
163
|
+
|
164
|
+
The differences:
|
165
|
+
|
166
|
+
* Performance. The "script" handler is faster then the "pure" handler.
|
167
|
+
The "pure" handler generates twice as much CPU load as "script" handler.
|
168
|
+
See {file:BENCHMARK.md BENCHMARK}.
|
169
|
+
|
170
|
+
* Expiration. The "script" implementation handler uses PEXPIREAT to mark semaphore life-time.
|
171
|
+
The "pure" handler stores semaphore expiry timestamp in key value.
|
172
|
+
Therefore the "script" handler can't refresh semaphores once they expire.
|
173
|
+
The "pure" handler on the other hand could refresh expired semaphore but only
|
174
|
+
if nothing else has locked on that expired key.
|
175
|
+
|
176
|
+
To detect feature of the current handler:
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
Redis::EM::Mutex.can_refresh_expired? # true / false
|
180
|
+
Redis::EM::Mutex.new(:foo).can_refresh_expired? # true / false
|
181
|
+
```
|
182
|
+
|
110
183
|
### Namespaces
|
111
184
|
|
112
185
|
```ruby
|
@@ -128,6 +201,17 @@ USAGE
|
|
128
201
|
|
129
202
|
### Multi-locks
|
130
203
|
|
204
|
+
This feature enables you to lock more then one key at the same time.
|
205
|
+
The multi key semaphores are deadlock-safe.
|
206
|
+
|
207
|
+
The classic deadlock example scenario with multiple resources:
|
208
|
+
|
209
|
+
* A acquires lock on resource :foo
|
210
|
+
* B acquires lock on resource :bar
|
211
|
+
* A tries to lock on resource :bar still keeping the :foo
|
212
|
+
* but at the same time B tries to acquire :foo while keeping the :bar.
|
213
|
+
* The deadlock occurs.
|
214
|
+
|
131
215
|
```ruby
|
132
216
|
EM.synchrony do
|
133
217
|
Redis::EM::Mutex.synchronize('foo', 'bar', 'baz') do
|
@@ -218,8 +302,8 @@ The locking scope will be Mutex global namespace + class name + method name.
|
|
218
302
|
|
219
303
|
### ConditionVariable
|
220
304
|
|
221
|
-
Redis::EM::Mutex may be used with EventMachine::Synchrony::Thread::ConditionVariable
|
222
|
-
in place of EventMachine::Synchrony::Thread::Mutex
|
305
|
+
`Redis::EM::Mutex` may be used with `EventMachine::Synchrony::Thread::ConditionVariable`
|
306
|
+
in place of `EventMachine::Synchrony::Thread::Mutex`.
|
223
307
|
|
224
308
|
```ruby
|
225
309
|
mutex = Redis::EM::Mutex.new('resource')
|
data/Rakefile
CHANGED
@@ -5,12 +5,31 @@ task :default => [:test]
|
|
5
5
|
$gem_name = "redis-em-mutex"
|
6
6
|
|
7
7
|
desc "Run spec tests"
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
namespace :test do
|
9
|
+
|
10
|
+
task :all => [:auto, :pure, :script]
|
11
|
+
|
12
|
+
task :auto do
|
13
|
+
Dir["spec/#{$gem_name}-*.rb"].each do |spec|
|
14
|
+
sh({'REDIS_EM_MUTEX_HANDLER' => nil}, "rspec #{spec}")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
task :pure do
|
19
|
+
Dir["spec/#{$gem_name}-*.rb"].each do |spec|
|
20
|
+
sh({'REDIS_EM_MUTEX_HANDLER' => 'pure'}, "rspec #{spec}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
task :script do
|
25
|
+
Dir["spec/#{$gem_name}-*.rb"].each do |spec|
|
26
|
+
sh({'REDIS_EM_MUTEX_HANDLER' => 'script'}, "rspec #{spec}")
|
27
|
+
end
|
11
28
|
end
|
12
29
|
end
|
13
30
|
|
31
|
+
task :test => [:'test:all']
|
32
|
+
|
14
33
|
desc "Build the gem"
|
15
34
|
task :gem do
|
16
35
|
sh "gem build #$gem_name.gemspec"
|
@@ -33,5 +52,5 @@ end
|
|
33
52
|
|
34
53
|
desc "Documentation"
|
35
54
|
task :doc do
|
36
|
-
sh "yardoc"
|
55
|
+
sh "yardoc - README.md BENCHMARK.md"
|
37
56
|
end
|
data/benchmark_mutex.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
$:.unshift "lib"
|
2
|
+
gem 'redis', '~>3.0.2'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'benchmark'
|
5
|
+
require 'em-synchrony'
|
6
|
+
require 'em-synchrony/fiber_iterator'
|
7
|
+
require 'redis-em-mutex'
|
8
|
+
|
9
|
+
RMutex = Redis::EM::Mutex
|
10
|
+
include Benchmark
|
11
|
+
|
12
|
+
MUTEX_OPTIONS = {
|
13
|
+
expire: 10000,
|
14
|
+
ns: '__Benchmark',
|
15
|
+
}
|
16
|
+
|
17
|
+
TEST_KEY = '__TEST__'
|
18
|
+
|
19
|
+
def assert(condition)
|
20
|
+
raise "Assertion failed: #{__FILE__}:#{__LINE__}" unless condition
|
21
|
+
end
|
22
|
+
|
23
|
+
# lock and unlock 1000 times
|
24
|
+
def test1(keys, concurrency = 10)
|
25
|
+
count = 0
|
26
|
+
mutex = RMutex.new(*keys)
|
27
|
+
EM::Synchrony::FiberIterator.new((1..1000).to_a, concurrency).each do |i|
|
28
|
+
mutex.synchronize { count+=1 }
|
29
|
+
end
|
30
|
+
assert(count == 1000)
|
31
|
+
end
|
32
|
+
|
33
|
+
# lock, set, incr, read, del, unlock, sleep as many times as possible in 5 seconds
|
34
|
+
# the cooldown period will be included in total time
|
35
|
+
def test2(keys, redis)
|
36
|
+
running = true
|
37
|
+
count = 0
|
38
|
+
playing = 0
|
39
|
+
mutex = RMutex.new(*keys)
|
40
|
+
f = Fiber.current
|
41
|
+
(1..100).map {|i| i/100000.0+0.001}.shuffle.each do |i|
|
42
|
+
EM::Synchrony.next_tick do
|
43
|
+
while running
|
44
|
+
playing+=1
|
45
|
+
EM::Synchrony.sleep(i)
|
46
|
+
mutex.synchronize do
|
47
|
+
# print "."
|
48
|
+
value = rand(1000000000000000000)
|
49
|
+
redis.set(TEST_KEY, value)
|
50
|
+
redis.incr(TEST_KEY)
|
51
|
+
assert redis.get(TEST_KEY).to_i == value+1
|
52
|
+
redis.del(TEST_KEY)
|
53
|
+
count += 1
|
54
|
+
end
|
55
|
+
playing-=1
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
EM::Synchrony.add_timer(5) do
|
60
|
+
running = false
|
61
|
+
# print "0"
|
62
|
+
EM::Synchrony.sleep(0.001) while playing > 0
|
63
|
+
EM.next_tick { f.resume }
|
64
|
+
end
|
65
|
+
Fiber.yield
|
66
|
+
print '%5d' % count
|
67
|
+
end
|
68
|
+
|
69
|
+
EM.synchrony do
|
70
|
+
concurrency = 10
|
71
|
+
RMutex.setup(MUTEX_OPTIONS) {|opts| opts.size = concurrency}
|
72
|
+
if RMutex.respond_to? :handler
|
73
|
+
puts "Version: #{RMutex::VERSION}, handler: #{RMutex.handler}"
|
74
|
+
else
|
75
|
+
puts "Version: #{RMutex::VERSION}, handler: N/A"
|
76
|
+
end
|
77
|
+
|
78
|
+
puts "lock/unlock 1000 times with concurrency: #{concurrency}"
|
79
|
+
Benchmark.benchmark(CAPTION, 7, FORMAT) do |x|
|
80
|
+
[1,2,3,5,10].each do |n|
|
81
|
+
keys = n.times.map { SecureRandom.random_bytes + '.lck' }
|
82
|
+
x.report("keys:%2d " % n) { test1(keys, concurrency) }
|
83
|
+
EM::Synchrony.sleep(1)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
puts
|
88
|
+
puts "lock/write/incr/read/del/unlock in 5 seconds + cooldown period:"
|
89
|
+
Benchmark.benchmark(CAPTION, 8, FORMAT) do |x|
|
90
|
+
redis = Redis.new
|
91
|
+
[1,2,3,5,10].each do |n|
|
92
|
+
keys = n.times.map { SecureRandom.random_bytes + '.lck' }
|
93
|
+
x.report("keys:%2d " % n) { test2(keys, redis) }
|
94
|
+
EM::Synchrony.sleep(1)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
RMutex.stop_watcher(true)
|
98
|
+
EM.stop
|
99
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'redis'
|
2
|
+
class Redis
|
3
|
+
module EM
|
4
|
+
class ConnectionPool
|
5
|
+
def initialize(opts)
|
6
|
+
@pool = []
|
7
|
+
@queue = []
|
8
|
+
@acquired = {}
|
9
|
+
|
10
|
+
opts[:size].times { @pool << yield }
|
11
|
+
end
|
12
|
+
|
13
|
+
%w[
|
14
|
+
exists
|
15
|
+
setnx
|
16
|
+
publish
|
17
|
+
script
|
18
|
+
msetnx
|
19
|
+
eval
|
20
|
+
evalsha
|
21
|
+
].each do |name|
|
22
|
+
class_eval <<-EOD, __FILE__, __LINE__
|
23
|
+
def #{name}(*args)
|
24
|
+
execute do |redis|
|
25
|
+
redis.#{name}(*args)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
EOD
|
29
|
+
end
|
30
|
+
|
31
|
+
%w[
|
32
|
+
watch
|
33
|
+
mget
|
34
|
+
].each do |name|
|
35
|
+
class_eval <<-EOD, __FILE__, __LINE__
|
36
|
+
def #{name}(*args, &blk)
|
37
|
+
execute do |redis|
|
38
|
+
redis.#{name}(*args, &blk)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
EOD
|
42
|
+
end
|
43
|
+
|
44
|
+
def multi(&blk)
|
45
|
+
execute do |redis|
|
46
|
+
redis.multi(&blk)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def execute
|
51
|
+
f = Fiber.current
|
52
|
+
begin
|
53
|
+
until (conn = acquire f)
|
54
|
+
@queue << f
|
55
|
+
Fiber.yield
|
56
|
+
end
|
57
|
+
yield conn
|
58
|
+
ensure
|
59
|
+
release(f)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def acquire(fiber)
|
66
|
+
if conn = @pool.pop
|
67
|
+
@acquired[fiber.__id__] = conn
|
68
|
+
conn
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def release(fiber)
|
73
|
+
@pool.push(@acquired.delete(fiber.__id__))
|
74
|
+
|
75
|
+
if queue = @queue.shift
|
76
|
+
queue.resume
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_missing(method, *args, &blk)
|
81
|
+
execute do |conn|
|
82
|
+
conn.__send__(method, *args, &blk)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
data/lib/redis/em-mutex/ns.rb
CHANGED