redis-em-mutex 0.2.3 → 0.3.0
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/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