mongo-lock 1.0.0 → 1.1.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.
- 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
|
+
[](http://badge.fury.io/rb/mongo-lock)
|
5
|
+
[](https://gemnasium.com/trakio/mongo-lock)
|
4
6
|
[](https://codeclimate.com/github/trakio/mongo-lock)
|
7
|
+
[](https://travis-ci.org/trakio/mongo-lock)
|
8
|
+
[](https://coveralls.io/r/trakio/mongo-lock)
|
9
|
+
[](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
|