mongo-lock 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.coveralls.yml +1 -0
- data/.travis.yml +7 -0
- data/Gemfile.lock +24 -0
- data/README.md +9 -4
- data/lib/mongo-lock.rb +88 -145
- data/lib/mongo-lock/class_convenience_methods.rb +33 -0
- data/lib/mongo-lock/configuration.rb +2 -2
- data/lib/mongo-lock/queries.rb +97 -0
- data/lib/mongo-lock/version.rb +1 -1
- data/mongo-lock.gemspec +2 -0
- data/spec/acquire_spec.rb +2 -2
- data/spec/acquired_spec.rb +2 -2
- data/spec/configuration_spec.rb +8 -8
- data/spec/configure_spec.rb +3 -3
- data/spec/expired_spec.rb +2 -3
- data/spec/extend_by_spec.rb +7 -7
- data/spec/release_spec.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- metadata +33 -2
- data/Rakefile +0 -8
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NjI3ZTJmYTQ0ODdmNTU3Nzc0ZDQ0OWJmZjAyYzRmYzFhY2U2YzJkNA==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
YjRhOGM3MDNlMGM1ZjYzN2RhZjc4NzdiY2E4ZmJlMzVhMDY5MDc2ZQ==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NTViMDE5MDI2MDhjOTFmOWJkNGM5ZGYzZjc1NjUxN2VkMjRiNTdlNTlmYTQy
|
10
|
+
NzgxYTg1NDk2ZWJkNGJiNzBkMzhjNWFmZTUzZjg4YTkxZDI5ZjVkZDcwZDUw
|
11
|
+
ZmY5ZjkyYzlhZDQ2OTRiNjlkM2M4Y2ZkMWYxNTIyOTM2ZDE2NTg=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
OGIwM2E1ZmRjZjQxZGUwOWZjMWNhYTU5MTRhNTk2MTAzZGY1ZmIxNjk4OGQ3
|
14
|
+
NDRjOWRlOGRjZTI1OWY5ZDcyMzNkYzAxMDI3NzVjYjU3YTQwMGUwOWYzNDAz
|
15
|
+
YWQxODEzZTIxYWY2NmQxNmI3OTlkZTY3ZTE2OWI4MTU5M2U0Nzk=
|
data/.coveralls.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
service_name: travis-ci
|
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -13,19 +13,32 @@ GEM
|
|
13
13
|
thread_safe (~> 0.1)
|
14
14
|
tzinfo (~> 0.3.37)
|
15
15
|
atomic (1.1.14)
|
16
|
+
bson (1.9.2)
|
16
17
|
coderay (1.1.0)
|
18
|
+
coveralls (0.7.0)
|
19
|
+
multi_json (~> 1.3)
|
20
|
+
rest-client
|
21
|
+
simplecov (>= 0.7)
|
22
|
+
term-ansicolor
|
23
|
+
thor
|
17
24
|
diff-lcs (1.2.5)
|
25
|
+
docile (1.1.3)
|
18
26
|
fuubar (1.3.2)
|
19
27
|
rspec (>= 2.14.0, < 3.1.0)
|
20
28
|
ruby-progressbar (~> 1.3)
|
21
29
|
i18n (0.6.9)
|
22
30
|
method_source (0.8.2)
|
31
|
+
mime-types (1.25.1)
|
23
32
|
minitest (4.7.5)
|
33
|
+
mongo (1.9.2)
|
34
|
+
bson (~> 1.9.2)
|
24
35
|
multi_json (1.8.4)
|
25
36
|
pry (0.9.12.6)
|
26
37
|
coderay (~> 1.0)
|
27
38
|
method_source (~> 0.8)
|
28
39
|
slop (~> 3.4)
|
40
|
+
rest-client (1.6.7)
|
41
|
+
mime-types (>= 1.16)
|
29
42
|
rspec (2.14.1)
|
30
43
|
rspec-core (~> 2.14.0)
|
31
44
|
rspec-expectations (~> 2.14.0)
|
@@ -35,9 +48,18 @@ GEM
|
|
35
48
|
diff-lcs (>= 1.1.3, < 2.0)
|
36
49
|
rspec-mocks (2.14.5)
|
37
50
|
ruby-progressbar (1.4.1)
|
51
|
+
simplecov (0.8.2)
|
52
|
+
docile (~> 1.1.0)
|
53
|
+
multi_json
|
54
|
+
simplecov-html (~> 0.8.0)
|
55
|
+
simplecov-html (0.8.0)
|
38
56
|
slop (3.4.7)
|
57
|
+
term-ansicolor (1.3.0)
|
58
|
+
tins (~> 1.0)
|
59
|
+
thor (0.18.1)
|
39
60
|
thread_safe (0.1.3)
|
40
61
|
atomic
|
62
|
+
tins (1.0.0)
|
41
63
|
tzinfo (0.3.38)
|
42
64
|
|
43
65
|
PLATFORMS
|
@@ -45,7 +67,9 @@ PLATFORMS
|
|
45
67
|
|
46
68
|
DEPENDENCIES
|
47
69
|
activesupport
|
70
|
+
coveralls
|
48
71
|
fuubar
|
72
|
+
mongo
|
49
73
|
mongo-lock!
|
50
74
|
pry
|
51
75
|
rspec
|
data/README.md
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
Mongo::Lock
|
2
2
|
==========
|
3
3
|
|
4
|
+
[![Gem Version](https://badge.fury.io/rb/mongo-lock.png)](http://badge.fury.io/rb/mongo-lock)
|
5
|
+
[![Dependency Status](https://gemnasium.com/trakio/mongo-lock.png)](https://gemnasium.com/trakio/mongo-lock)
|
4
6
|
[![Code Climate](https://codeclimate.com/github/trakio/mongo-lock.png)](https://codeclimate.com/github/trakio/mongo-lock)
|
7
|
+
[![Build Status](https://travis-ci.org/trakio/mongo-lock.png?branch=master)](https://travis-ci.org/trakio/mongo-lock)
|
8
|
+
[![Coverage Status](https://coveralls.io/repos/trakio/mongo-lock/badge.png)](https://coveralls.io/r/trakio/mongo-lock)
|
9
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/trakio/mongo-lock/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
5
10
|
|
6
11
|
Key based pessimistic locking for Ruby and MongoDB. Is this key avaliable? Yes - Lock it for me for a sec will you. No - OK I'll just wait here until its ready.
|
7
12
|
|
@@ -103,7 +108,7 @@ A lock will automatically be relinquished once its expiry has passed. Expired lo
|
|
103
108
|
|
104
109
|
```ruby
|
105
110
|
Mongo::Lock.configure do |config|
|
106
|
-
config.
|
111
|
+
config.expire_in = false # timeout_in in seconds for lock expiry; this defaults to 10.
|
107
112
|
end
|
108
113
|
```
|
109
114
|
|
@@ -188,7 +193,7 @@ Mongo::Lock.new 'my_key', {
|
|
188
193
|
timeout_in: 10, # timeout_in in seconds on acquisition; this defaults to false ie no time limit.
|
189
194
|
limit: 10, # The limit on the number of acquisition attempts; this defaults to 100.
|
190
195
|
frequency: 2, # Frequency in seconds for acquisition attempts ; this defaults to 1.
|
191
|
-
|
196
|
+
expire_in: 10,# timeout_in in seconds for lock expiry; this defaults to 10.
|
192
197
|
}
|
193
198
|
```
|
194
199
|
|
@@ -202,7 +207,7 @@ Mongo::Lock.new 'my_key' do |lock|
|
|
202
207
|
end
|
203
208
|
```
|
204
209
|
|
205
|
-
You can also call Mongo::Lock#extend and it will extend by the lock's
|
210
|
+
You can also call Mongo::Lock#extend and it will extend by the lock's expire_in option.
|
206
211
|
|
207
212
|
```ruby
|
208
213
|
Mongo::Lock.new 'my_key' do |lock|
|
@@ -213,7 +218,7 @@ end
|
|
213
218
|
### Check you still hold a lock
|
214
219
|
|
215
220
|
```ruby
|
216
|
-
Mongo::Lock.acquire 'my_key',
|
221
|
+
Mongo::Lock.acquire 'my_key', expire_in: 10 do |lock|
|
217
222
|
sleep 9
|
218
223
|
lock.expired? # False
|
219
224
|
sleep 11
|
data/lib/mongo-lock.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
require 'mongo-lock/configuration'
|
2
|
+
require 'mongo-lock/queries'
|
3
|
+
require 'mongo-lock/class_convenience_methods'
|
2
4
|
|
3
5
|
module Mongo
|
4
6
|
class Lock
|
5
7
|
|
8
|
+
extend Mongo::Lock::ClassConvenienceMethods
|
9
|
+
|
6
10
|
class NotAcquiredError < StandardError ; end
|
7
11
|
class NotReleasedError < StandardError ; end
|
8
12
|
class NotExtendedError < StandardError ; end
|
@@ -12,13 +16,14 @@ module Mongo
|
|
12
16
|
attr_accessor :acquired
|
13
17
|
attr_accessor :expires_at
|
14
18
|
attr_accessor :released
|
19
|
+
attr_accessor :query
|
15
20
|
|
16
21
|
def self.configure options = {}, &block
|
17
22
|
defaults = {
|
18
|
-
timeout_in:
|
23
|
+
timeout_in: false,
|
19
24
|
limit: 100,
|
20
25
|
frequency: 1,
|
21
|
-
|
26
|
+
expire_in: 10,
|
22
27
|
raise: false,
|
23
28
|
owner: Proc.new { "#{`hostname`.strip}:#{Process.pid}:#{Thread.object_id}" }
|
24
29
|
}
|
@@ -34,124 +39,87 @@ module Mongo
|
|
34
39
|
end
|
35
40
|
end
|
36
41
|
|
42
|
+
|
37
43
|
def self.release_all options = {}
|
38
44
|
if options.include? :collection
|
39
|
-
release_collection configuration.collection(options[:collection]), options[:owner]
|
45
|
+
Mongo::Lock::Queries.release_collection configuration.collection(options[:collection]), options[:owner]
|
40
46
|
else
|
41
47
|
configuration.collections.each_pair do |key,collection|
|
42
|
-
release_collection collection, options[:owner]
|
48
|
+
Mongo::Lock::Queries.release_collection collection, options[:owner]
|
43
49
|
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
|
-
def self.release_collection collection, owner=nil
|
48
|
-
selector = if owner then { owner: owner } else {} end
|
49
|
-
collection.remove(selector)
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.init_and_send key, options = {}, method
|
53
|
-
lock = self.new(key, options)
|
54
|
-
lock.send(method)
|
55
|
-
lock
|
56
|
-
end
|
57
|
-
|
58
|
-
def self.acquire key, options = {}
|
59
|
-
init_and_send key, options, :acquire
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.release key, options = {}
|
63
|
-
init_and_send key, options, :release
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.acquire! key, options = {}
|
67
|
-
init_and_send key, options, :acquire!
|
68
|
-
end
|
69
|
-
|
70
|
-
def self.release! key, options = {}
|
71
|
-
init_and_send key, options, :release!
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.available? key, options = {}
|
75
|
-
init_and_send key, options, :available?
|
76
|
-
end
|
77
|
-
|
78
53
|
def self.ensure_indexes
|
79
54
|
configuration.collections.each_pair do |key, collection|
|
80
|
-
collection
|
81
|
-
['key', Mongo::ASCENDING],
|
82
|
-
['owner', Mongo::ASCENDING],
|
83
|
-
['expires_at', Mongo::ASCENDING]
|
84
|
-
])
|
85
|
-
collection.create_index([['ttl', Mongo::ASCENDING]],{ expireAfterSeconds: 0 })
|
55
|
+
Mongo::Lock::Queries.ensure_indexes collection
|
86
56
|
end
|
87
57
|
end
|
88
58
|
|
89
59
|
def self.clear_expired
|
90
60
|
configuration.collections.each_pair do |key,collection|
|
91
|
-
|
61
|
+
Mongo::Lock::Queries.clear_expired collection
|
92
62
|
end
|
93
63
|
end
|
94
64
|
|
65
|
+
|
95
66
|
def initialize key, options = {}
|
96
67
|
self.configuration = Configuration.new self.class.configuration.to_hash, options
|
97
68
|
self.key = key
|
69
|
+
self.query = Mongo::Lock::Queries.new self
|
98
70
|
acquire_if_acquired
|
99
71
|
end
|
100
72
|
|
73
|
+
# API
|
74
|
+
|
101
75
|
def configure options = {}, &block
|
102
76
|
self.configuration = Configuration.new self.configuration.to_hash, options
|
103
77
|
yield self.configuration if block_given?
|
104
78
|
end
|
105
79
|
|
106
80
|
def acquire options = {}
|
107
|
-
options =
|
81
|
+
options = inherit_options options
|
108
82
|
i = 1
|
109
83
|
time_spent = 0
|
110
84
|
|
111
85
|
loop do
|
112
|
-
|
113
|
-
|
114
|
-
return raise_or_false options
|
86
|
+
result = try_acquire options, i, time_spent
|
87
|
+
return result unless result.nil?
|
115
88
|
|
116
|
-
|
117
|
-
|
118
|
-
|
89
|
+
frequency = call_if_proc options[:frequency], i
|
90
|
+
sleep frequency
|
91
|
+
time_spent += frequency
|
92
|
+
i += 1
|
93
|
+
end
|
94
|
+
end
|
119
95
|
|
120
|
-
|
121
|
-
|
96
|
+
def try_acquire options, i, time_spent
|
97
|
+
# If timeout has expired
|
98
|
+
if options[:timeout_in] && options[:timeout_in] < time_spent
|
99
|
+
return raise_or_false options
|
122
100
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
extend_by options[:expires_after]
|
127
|
-
return true
|
128
|
-
end
|
101
|
+
# If limit has expired
|
102
|
+
elsif options[:limit] && options[:limit] < i
|
103
|
+
return raise_or_false options
|
129
104
|
|
130
|
-
|
131
|
-
|
105
|
+
# If there is an existing lock
|
106
|
+
elsif existing_lock = query.find_or_insert(options)
|
107
|
+
# If the lock is owned by me
|
108
|
+
if existing_lock['owner'] == options[:owner]
|
132
109
|
self.acquired = true
|
110
|
+
extend_by options[:expire_in]
|
133
111
|
return true
|
134
|
-
|
135
112
|
end
|
136
113
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
142
|
-
sleep frequency
|
143
|
-
time_spent += frequency
|
144
|
-
i += 1
|
114
|
+
# If the lock was acquired
|
115
|
+
else
|
116
|
+
self.acquired = true
|
117
|
+
return true
|
145
118
|
end
|
146
119
|
end
|
147
120
|
|
148
|
-
def acquire! options = {}
|
149
|
-
options[:raise] = true
|
150
|
-
acquire options
|
151
|
-
end
|
152
|
-
|
153
121
|
def release options = {}
|
154
|
-
options =
|
122
|
+
options = inherit_options options
|
155
123
|
|
156
124
|
# If the lock has already been released
|
157
125
|
if released?
|
@@ -174,47 +142,13 @@ module Mongo
|
|
174
142
|
else
|
175
143
|
self.released = true
|
176
144
|
self.acquired = false
|
177
|
-
|
145
|
+
query.remove options
|
178
146
|
return true
|
179
147
|
end
|
180
148
|
end
|
181
149
|
|
182
|
-
def release! options = {}
|
183
|
-
options[:raise] = true
|
184
|
-
release options
|
185
|
-
end
|
186
|
-
|
187
|
-
def raise_or_false options, error = NotAcquiredError
|
188
|
-
raise error if options[:raise]
|
189
|
-
false
|
190
|
-
end
|
191
|
-
|
192
|
-
def find_or_insert options
|
193
|
-
to_expire_at = Time.now + options[:expires_after]
|
194
|
-
existing_lock = collection.find_and_modify({
|
195
|
-
query: query,
|
196
|
-
update: {
|
197
|
-
'$setOnInsert' => {
|
198
|
-
key: key,
|
199
|
-
owner: options[:owner],
|
200
|
-
expires_at: to_expire_at,
|
201
|
-
ttl: to_expire_at
|
202
|
-
}
|
203
|
-
},
|
204
|
-
upsert: true
|
205
|
-
})
|
206
|
-
|
207
|
-
if existing_lock
|
208
|
-
self.expires_at = existing_lock['expires_at']
|
209
|
-
else
|
210
|
-
self.expires_at = to_expire_at
|
211
|
-
end
|
212
|
-
|
213
|
-
existing_lock
|
214
|
-
end
|
215
|
-
|
216
150
|
def extend_by time, options = {}
|
217
|
-
options =
|
151
|
+
options = inherit_options options
|
218
152
|
|
219
153
|
# Can't extend a lock that hasn't been acquired
|
220
154
|
if !acquired?
|
@@ -225,51 +159,42 @@ module Mongo
|
|
225
159
|
return raise_or_false options, NotExtendedError
|
226
160
|
|
227
161
|
else
|
228
|
-
|
229
|
-
existing_lock = collection.find_and_modify({
|
230
|
-
query: query,
|
231
|
-
update: {
|
232
|
-
'$set' => {
|
233
|
-
key: key,
|
234
|
-
owner: options[:owner],
|
235
|
-
expires_at: to_expire_at,
|
236
|
-
ttl: to_expire_at
|
237
|
-
}
|
238
|
-
},
|
239
|
-
upsert: true
|
240
|
-
})
|
162
|
+
query.find_and_update time, options
|
241
163
|
true
|
242
164
|
end
|
243
165
|
end
|
244
166
|
|
245
167
|
def extend options = {}
|
246
|
-
time = configuration.to_hash.merge(options)[:
|
168
|
+
time = configuration.to_hash.merge(options)[:expire_in]
|
247
169
|
extend_by time, options
|
248
170
|
end
|
249
171
|
|
250
|
-
def
|
251
|
-
options
|
252
|
-
|
172
|
+
def available? options = {}
|
173
|
+
options = inherit_options options
|
174
|
+
existing_lock = query.find_existing
|
175
|
+
!existing_lock || existing_lock['owner'] == options[:owner]
|
253
176
|
end
|
254
177
|
|
255
|
-
|
256
|
-
|
257
|
-
|
178
|
+
# Raise methods
|
179
|
+
|
180
|
+
def acquire! options = {}
|
181
|
+
send_with_raise :acquire, options
|
258
182
|
end
|
259
183
|
|
260
|
-
def
|
261
|
-
|
262
|
-
existing_lock = collection.find(query).first
|
263
|
-
!existing_lock || existing_lock['owner'] == options[:owner]
|
184
|
+
def release! options = {}
|
185
|
+
send_with_raise :release, options
|
264
186
|
end
|
265
187
|
|
266
|
-
def
|
267
|
-
|
268
|
-
key: key,
|
269
|
-
expires_at: { '$gt' => Time.now }
|
270
|
-
}
|
188
|
+
def extend_by! time, options = {}
|
189
|
+
send_with_raise :extend_by, time, options
|
271
190
|
end
|
272
191
|
|
192
|
+
def extend! options = {}
|
193
|
+
send_with_raise :extend, options
|
194
|
+
end
|
195
|
+
|
196
|
+
# Current state
|
197
|
+
|
273
198
|
def acquired?
|
274
199
|
!!acquired && !expired?
|
275
200
|
end
|
@@ -282,13 +207,31 @@ module Mongo
|
|
282
207
|
!!released
|
283
208
|
end
|
284
209
|
|
210
|
+
# Utils
|
211
|
+
|
285
212
|
def acquire_if_acquired
|
286
|
-
if
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
213
|
+
self.acquired = true if query.is_acquired?
|
214
|
+
end
|
215
|
+
|
216
|
+
def send_with_raise method, *args
|
217
|
+
args.last[:raise] = true
|
218
|
+
self.send(method, *args)
|
219
|
+
end
|
220
|
+
|
221
|
+
def raise_or_false options, error = NotAcquiredError
|
222
|
+
raise error if options[:raise]
|
223
|
+
false
|
224
|
+
end
|
225
|
+
|
226
|
+
def inherit_options options
|
227
|
+
configuration.to_hash.merge options
|
228
|
+
end
|
229
|
+
|
230
|
+
def call_if_proc proc, *args
|
231
|
+
if proc.is_a? Proc
|
232
|
+
proc.call(*args)
|
233
|
+
else
|
234
|
+
proc
|
292
235
|
end
|
293
236
|
end
|
294
237
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mongo
|
2
|
+
class Lock
|
3
|
+
module ClassConvenienceMethods
|
4
|
+
|
5
|
+
def init_and_send key, options = {}, method
|
6
|
+
lock = Mongo::Lock.new(key, options)
|
7
|
+
lock.send(method)
|
8
|
+
lock
|
9
|
+
end
|
10
|
+
|
11
|
+
def acquire key, options = {}
|
12
|
+
init_and_send key, options, :acquire
|
13
|
+
end
|
14
|
+
|
15
|
+
def release key, options = {}
|
16
|
+
init_and_send key, options, :release
|
17
|
+
end
|
18
|
+
|
19
|
+
def acquire! key, options = {}
|
20
|
+
init_and_send key, options, :acquire!
|
21
|
+
end
|
22
|
+
|
23
|
+
def release! key, options = {}
|
24
|
+
init_and_send key, options, :release!
|
25
|
+
end
|
26
|
+
|
27
|
+
def available? key, options = {}
|
28
|
+
init_and_send key, options, :available?
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -6,7 +6,7 @@ module Mongo
|
|
6
6
|
attr_accessor :limit
|
7
7
|
attr_accessor :timeout_in
|
8
8
|
attr_accessor :frequency
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :expire_in
|
10
10
|
attr_accessor :owner
|
11
11
|
attr_accessor :raise
|
12
12
|
|
@@ -53,7 +53,7 @@ module Mongo
|
|
53
53
|
timeout_in: timeout_in,
|
54
54
|
limit: limit,
|
55
55
|
frequency: frequency,
|
56
|
-
|
56
|
+
expire_in: expire_in,
|
57
57
|
owner: owner,
|
58
58
|
raise: raise
|
59
59
|
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Mongo
|
2
|
+
class Lock
|
3
|
+
class Queries
|
4
|
+
|
5
|
+
attr_accessor :lock
|
6
|
+
|
7
|
+
def self.release_collection collection, owner=nil
|
8
|
+
selector = if owner then { owner: owner } else {} end
|
9
|
+
collection.remove(selector)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.ensure_indexes collection
|
13
|
+
collection.create_index([
|
14
|
+
['key', Mongo::ASCENDING],
|
15
|
+
['owner', Mongo::ASCENDING],
|
16
|
+
['expires_at', Mongo::ASCENDING]
|
17
|
+
])
|
18
|
+
collection.create_index([['ttl', Mongo::ASCENDING]],{ expireAfterSeconds: 0 })
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.clear_expired collection
|
22
|
+
collection.remove expires_at: { '$lt' => Time.now }
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize lock
|
26
|
+
self.lock = lock
|
27
|
+
end
|
28
|
+
|
29
|
+
def key
|
30
|
+
lock.key
|
31
|
+
end
|
32
|
+
|
33
|
+
def query
|
34
|
+
{
|
35
|
+
key: key,
|
36
|
+
expires_at: { '$gt' => Time.now }
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def find_or_insert options
|
41
|
+
options[:expire_at] = Time.now + options[:expire_in]
|
42
|
+
options[:insert] = true
|
43
|
+
find_and_modify options
|
44
|
+
end
|
45
|
+
|
46
|
+
def find_and_update time, options
|
47
|
+
options[:expire_at] = lock.expires_at + time
|
48
|
+
find_and_modify options
|
49
|
+
end
|
50
|
+
|
51
|
+
def find_and_modify options
|
52
|
+
operation = options[:insert] ? '$setOnInsert' : '$set'
|
53
|
+
existing_lock = collection.find_and_modify({
|
54
|
+
query: query,
|
55
|
+
update: {
|
56
|
+
operation => {
|
57
|
+
key: key,
|
58
|
+
owner: options[:owner],
|
59
|
+
expires_at: options[:expire_at],
|
60
|
+
ttl: options[:expire_at]
|
61
|
+
}
|
62
|
+
},
|
63
|
+
upsert: !!options[:insert]
|
64
|
+
})
|
65
|
+
|
66
|
+
if existing_lock
|
67
|
+
lock.expires_at = existing_lock['expires_at']
|
68
|
+
else
|
69
|
+
lock.expires_at = options[:expire_at]
|
70
|
+
end
|
71
|
+
|
72
|
+
existing_lock
|
73
|
+
end
|
74
|
+
|
75
|
+
def remove options
|
76
|
+
collection.remove key: key, owner: options[:owner]
|
77
|
+
end
|
78
|
+
|
79
|
+
def is_acquired?
|
80
|
+
find_already_acquired.count > 0
|
81
|
+
end
|
82
|
+
|
83
|
+
def find_already_acquired
|
84
|
+
collection.find({
|
85
|
+
key: key,
|
86
|
+
owner: lock.configuration.owner,
|
87
|
+
expires_at: { '$gt' => Time.now }
|
88
|
+
})
|
89
|
+
end
|
90
|
+
|
91
|
+
def find_existing
|
92
|
+
collection.find(query).first
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
data/lib/mongo-lock/version.rb
CHANGED
data/mongo-lock.gemspec
CHANGED
@@ -23,8 +23,10 @@ Gem::Specification.new do |spec|
|
|
23
23
|
spec.extra_rdoc_files = ['README.md', 'LICENSE']
|
24
24
|
|
25
25
|
spec.add_development_dependency 'rspec'
|
26
|
+
spec.add_development_dependency 'mongo'
|
26
27
|
spec.add_development_dependency 'fuubar'
|
27
28
|
spec.add_development_dependency 'pry'
|
28
29
|
spec.add_development_dependency 'activesupport'
|
30
|
+
spec.add_development_dependency 'coveralls'
|
29
31
|
|
30
32
|
end
|
data/spec/acquire_spec.rb
CHANGED
@@ -52,7 +52,7 @@ describe Mongo::Lock do
|
|
52
52
|
let(:lock) { Mongo::Lock.new 'my_lock' }
|
53
53
|
|
54
54
|
it "should call the Proc with the attempt number" do
|
55
|
-
collection.insert key: 'my_lock', owner: 'tobie', expires_at:
|
55
|
+
collection.insert key: 'my_lock', owner: 'tobie', expires_at: 10.seconds.from_now
|
56
56
|
proc = Proc.new{ |x| x }
|
57
57
|
expect(proc).to receive(:call).with(1).and_return(0.01)
|
58
58
|
expect(proc).to receive(:call).with(2).and_return(0.01)
|
@@ -124,7 +124,7 @@ describe Mongo::Lock do
|
|
124
124
|
|
125
125
|
context "and acquisition timeout_in occurs" do
|
126
126
|
|
127
|
-
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'tobie', timeout_in: 0.4, frequency: 0.01, raise: true }
|
127
|
+
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'tobie', timeout_in: 0.4, limit: 3, frequency: 0.01, raise: true }
|
128
128
|
|
129
129
|
it "should raise Mongo::Lock::NotAcquiredError" do
|
130
130
|
collection.insert key: 'my_lock', owner: 'spence', expires_at: 0.2.seconds.from_now
|
data/spec/acquired_spec.rb
CHANGED
@@ -28,9 +28,9 @@ describe Mongo::Lock do
|
|
28
28
|
context "when the lock was acquired but has since expired" do
|
29
29
|
|
30
30
|
it "returns false" do
|
31
|
-
collection.insert key: 'my_lock', owner: 'spence', expires_at: 0.
|
31
|
+
collection.insert key: 'my_lock', owner: 'spence', expires_at: 0.1.seconds.from_now
|
32
32
|
lock.acquire
|
33
|
-
sleep 0.
|
33
|
+
sleep 0.2
|
34
34
|
expect(lock.acquired?).to be_false
|
35
35
|
end
|
36
36
|
|
data/spec/configuration_spec.rb
CHANGED
@@ -188,20 +188,20 @@ describe Mongo::Lock::Configuration do
|
|
188
188
|
|
189
189
|
end
|
190
190
|
|
191
|
-
describe "#
|
191
|
+
describe "#expire_in=" do
|
192
192
|
|
193
|
-
it "should set the
|
194
|
-
subject.
|
195
|
-
expect(subject.instance_variable_get('@
|
193
|
+
it "should set the expire_in value" do
|
194
|
+
subject.expire_in = 9
|
195
|
+
expect(subject.instance_variable_get('@expire_in')).to be 9
|
196
196
|
end
|
197
197
|
|
198
198
|
end
|
199
199
|
|
200
|
-
describe "#
|
200
|
+
describe "#expire_in" do
|
201
201
|
|
202
|
-
it "should return the
|
203
|
-
subject.instance_variable_set('@
|
204
|
-
expect(subject.
|
202
|
+
it "should return the expire_in value" do
|
203
|
+
subject.instance_variable_set('@expire_in', 1)
|
204
|
+
expect(subject.expire_in).to be 1
|
205
205
|
end
|
206
206
|
|
207
207
|
end
|
data/spec/configure_spec.rb
CHANGED
@@ -37,15 +37,15 @@ describe Mongo::Lock do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
it "sets default timeout_in" do
|
40
|
-
expect(Mongo::Lock.configuration.timeout_in).to be
|
40
|
+
expect(Mongo::Lock.configuration.timeout_in).to be false
|
41
41
|
end
|
42
42
|
|
43
43
|
it "sets default frequency" do
|
44
44
|
expect(Mongo::Lock.configuration.frequency).to be 1
|
45
45
|
end
|
46
46
|
|
47
|
-
it "sets default
|
48
|
-
expect(Mongo::Lock.configuration.
|
47
|
+
it "sets default expire_in" do
|
48
|
+
expect(Mongo::Lock.configuration.expire_in).to be 10
|
49
49
|
end
|
50
50
|
|
51
51
|
it "sets default owner" do
|
data/spec/expired_spec.rb
CHANGED
@@ -18,7 +18,7 @@ describe Mongo::Lock do
|
|
18
18
|
context "when the lock has expired" do
|
19
19
|
|
20
20
|
it "returns true" do
|
21
|
-
lock.acquire
|
21
|
+
lock.acquire expire_in: 0.01
|
22
22
|
sleep 0.02
|
23
23
|
expect(lock.expired?).to be_true
|
24
24
|
end
|
@@ -28,8 +28,7 @@ describe Mongo::Lock do
|
|
28
28
|
context "when the lock hasn't expired" do
|
29
29
|
|
30
30
|
it "returns false" do
|
31
|
-
lock.acquire
|
32
|
-
sleep 0.01
|
31
|
+
lock.acquire expire_in: 0.1
|
33
32
|
expect(lock.expired?).to be_false
|
34
33
|
end
|
35
34
|
|
data/spec/extend_by_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Mongo::Lock do
|
4
4
|
|
5
|
-
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'spence', timeout_in: 0.01, frequency: 0.01,
|
5
|
+
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'spence', timeout_in: 0.01, frequency: 0.01, expire_in: 0.1 }
|
6
6
|
|
7
7
|
describe '#extend_by' do
|
8
8
|
|
@@ -25,7 +25,7 @@ describe Mongo::Lock do
|
|
25
25
|
|
26
26
|
it "returns false" do
|
27
27
|
lock.acquire
|
28
|
-
sleep 0.
|
28
|
+
sleep 0.11
|
29
29
|
expect(lock.extend_by 10).to be_false
|
30
30
|
end
|
31
31
|
|
@@ -52,7 +52,7 @@ describe Mongo::Lock do
|
|
52
52
|
|
53
53
|
context "when the raise option is set to true" do
|
54
54
|
|
55
|
-
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'spence', timeout_in: 0.01, frequency: 0.01,
|
55
|
+
let(:lock) { Mongo::Lock.new 'my_lock', owner: 'spence', timeout_in: 0.01, frequency: 0.01, expire_in: 0.01, raise: true }
|
56
56
|
|
57
57
|
context "and the lock has expired" do
|
58
58
|
|
@@ -100,13 +100,13 @@ describe Mongo::Lock do
|
|
100
100
|
|
101
101
|
describe '#extend' do
|
102
102
|
|
103
|
-
it "calls #extend_by with the default
|
104
|
-
expect(lock).to receive(:extend_by).with(lock.configuration.
|
103
|
+
it "calls #extend_by with the default expire_in config setting" do
|
104
|
+
expect(lock).to receive(:extend_by).with(lock.configuration.expire_in, {})
|
105
105
|
lock.extend
|
106
106
|
end
|
107
107
|
|
108
108
|
it "also passes options on" do
|
109
|
-
expect(lock).to receive(:extend_by).with(lock.configuration.
|
109
|
+
expect(lock).to receive(:extend_by).with(lock.configuration.expire_in, { raise: true })
|
110
110
|
lock.extend raise: true
|
111
111
|
end
|
112
112
|
|
@@ -124,7 +124,7 @@ describe Mongo::Lock do
|
|
124
124
|
describe '#extend!' do
|
125
125
|
|
126
126
|
it "calls .extend with raise errors option set to true" do
|
127
|
-
expect(lock).to receive(:extend_by).with(lock.configuration.
|
127
|
+
expect(lock).to receive(:extend_by).with(lock.configuration.expire_in, { raise: true })
|
128
128
|
lock.extend!
|
129
129
|
end
|
130
130
|
|
data/spec/release_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Mongo::Lock do
|
4
4
|
|
5
|
-
let(:lock) { Mongo::Lock.acquire('my_lock', owner: 'spence',
|
5
|
+
let(:lock) { Mongo::Lock.acquire('my_lock', owner: 'spence', expire_in: 0.1.seconds, timeout_in: 0.01, frequency: 0.01) }
|
6
6
|
|
7
7
|
describe '.release' do
|
8
8
|
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo-lock
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Spence
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mongo
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: fuubar
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +80,20 @@ dependencies:
|
|
66
80
|
- - ! '>='
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: coveralls
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
69
97
|
description: Key based pessimistic locking for Ruby and MongoDB. Is this key avaliable?
|
70
98
|
Yes - Lock it for me for a sec will you. No - OK I'll just wait here until its ready.
|
71
99
|
email: msaspence@gmail.com
|
@@ -75,14 +103,17 @@ extra_rdoc_files:
|
|
75
103
|
- README.md
|
76
104
|
- LICENSE
|
77
105
|
files:
|
106
|
+
- .coveralls.yml
|
78
107
|
- .gitignore
|
108
|
+
- .travis.yml
|
79
109
|
- Gemfile
|
80
110
|
- Gemfile.lock
|
81
111
|
- LICENSE
|
82
112
|
- README.md
|
83
|
-
- Rakefile
|
84
113
|
- lib/mongo-lock.rb
|
114
|
+
- lib/mongo-lock/class_convenience_methods.rb
|
85
115
|
- lib/mongo-lock/configuration.rb
|
116
|
+
- lib/mongo-lock/queries.rb
|
86
117
|
- lib/mongo-lock/version.rb
|
87
118
|
- mongo-lock.gemspec
|
88
119
|
- mongoid.yml
|