just_one_lock 0.0.1
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 +7 -0
- data/.bundle/config +3 -0
- data/.gitignore +2 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +21 -0
- data/README.md +4 -0
- data/Rakefile +1 -0
- data/just_one_lock.gemspec +24 -0
- data/lib/just_one_lock.rb +23 -0
- data/lib/just_one_lock/version.rb +3 -0
- data/spec/just_one_lock_spec.rb +246 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: bd505c4500de6a9d5ac9fca007c2d204bfe2f6a5
|
4
|
+
data.tar.gz: 2817838753a95afe43173d5e867adfb8f9006d8d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 661c30672498de83b0380075cb0629e9d003ae132c41f906b3a90a171adfb6a0037f95f3fed7720a21fb16a08cddd545701cd26d4a6f4d7aee9046bffbb57439
|
7
|
+
data.tar.gz: ccc5ae6dfde9a056f54d551f2084c7d6796318611ad01ee9c48fc3058ba8decdf4f750c2362128b57829df75ca36b8eec3877d903bb89990baaa066a0ea5f219
|
data/.bundle/config
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
just_one_lock (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.2.5)
|
10
|
+
rake (10.3.2)
|
11
|
+
rspec (3.0.0)
|
12
|
+
rspec-core (~> 3.0.0)
|
13
|
+
rspec-expectations (~> 3.0.0)
|
14
|
+
rspec-mocks (~> 3.0.0)
|
15
|
+
rspec-core (3.0.4)
|
16
|
+
rspec-support (~> 3.0.0)
|
17
|
+
rspec-expectations (3.0.4)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.0.0)
|
20
|
+
rspec-mocks (3.0.4)
|
21
|
+
rspec-support (~> 3.0.0)
|
22
|
+
rspec-support (3.0.4)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler (~> 1.3)
|
29
|
+
just_one_lock!
|
30
|
+
rake
|
31
|
+
rspec
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 beorc
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'just_one_lock/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "just_one_lock"
|
8
|
+
spec.version = JustOneLock::VERSION
|
9
|
+
spec.authors = ["Adam Stankiewicz, Yury Kotov"]
|
10
|
+
spec.email = ["bairkan@gmail.com"]
|
11
|
+
spec.description = %q{Simple solution to prevent multiple executions using flock}
|
12
|
+
spec.summary = %q{Simple solution to prevent multiple executions using flock}
|
13
|
+
spec.homepage = "http://github.com/beorc/just_one_lock"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake", '~> 10.3'
|
23
|
+
spec.add_development_dependency "rspec", '~> 3.0'
|
24
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'just_one_lock/version'
|
2
|
+
require 'timeout'
|
3
|
+
|
4
|
+
module JustOneLock
|
5
|
+
def self.filelock(lockname, options = {}, &block)
|
6
|
+
File.open(lockname, File::RDWR|File::CREAT, 0644) do |file|
|
7
|
+
Timeout::timeout(options.fetch(:timeout, 60)) { file.flock(File::LOCK_EX) }
|
8
|
+
yield
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.prevent_multiple_executions(lock_dir, scope, &block)
|
13
|
+
scope_name = scope.gsub(':', '_')
|
14
|
+
lock_path = File.join(lock_dir, scope_name + '.lock')
|
15
|
+
|
16
|
+
begin
|
17
|
+
return filelock(lock_path, &block)
|
18
|
+
rescue Timeout::Error => e
|
19
|
+
puts "Another process <#{scope}> already is running"
|
20
|
+
exit(1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'just_one_lock'
|
2
|
+
require 'tempfile'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
describe JustOneLock do
|
6
|
+
|
7
|
+
# Helper because File.write won't work for older Ruby
|
8
|
+
def write(filename, contents)
|
9
|
+
File.open(filename.to_s, 'w') { |f| f.write(contents) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def parallel(n = 2, templock = nil, &block)
|
13
|
+
Timeout::timeout(5) do
|
14
|
+
templock ||= Tempfile.new(['sample', '.lock'])
|
15
|
+
|
16
|
+
(1..n).map do
|
17
|
+
Thread.new do
|
18
|
+
JustOneLock.filelock(templock, &block)
|
19
|
+
end
|
20
|
+
end.map(&:join)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def parallel_forks(n = 2, templock = nil, &block)
|
25
|
+
Timeout::timeout(5) do
|
26
|
+
templock ||= Tempfile.new(['sample', '.lock'])
|
27
|
+
|
28
|
+
(1..n).map do
|
29
|
+
fork {
|
30
|
+
JustOneLock.filelock(templock, &block)
|
31
|
+
}
|
32
|
+
end.map do |pid|
|
33
|
+
Process.waitpid(pid)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'runs simple ruby block as usual' do
|
39
|
+
Dir.mktmpdir do |dir|
|
40
|
+
lockpath = File.join(dir, 'sample.lock')
|
41
|
+
answer = 0
|
42
|
+
|
43
|
+
JustOneLock.filelock lockpath do
|
44
|
+
answer += 42
|
45
|
+
end
|
46
|
+
|
47
|
+
expect(answer).to eq(42)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'returns value returned by block' do
|
52
|
+
Dir.mktmpdir do |dir|
|
53
|
+
lockpath = File.join(dir, 'sample.lock')
|
54
|
+
answer = 0
|
55
|
+
|
56
|
+
answer = JustOneLock.filelock lockpath do
|
57
|
+
42
|
58
|
+
end
|
59
|
+
|
60
|
+
expect(answer).to eq(42)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'runs in parallel without race condition' do
|
65
|
+
answer = 0
|
66
|
+
|
67
|
+
parallel(2) do
|
68
|
+
value = answer
|
69
|
+
sleep 0.1
|
70
|
+
answer = value + 21
|
71
|
+
end
|
72
|
+
|
73
|
+
expect(answer).to eq(42)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'handels high amount of concurrent tasks' do
|
77
|
+
answer = 0
|
78
|
+
|
79
|
+
parallel(100) do
|
80
|
+
value = answer
|
81
|
+
sleep 0.001
|
82
|
+
answer = value + 1
|
83
|
+
end
|
84
|
+
|
85
|
+
expect(answer).to eq(100)
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'creates lock file on disk during block execution' do
|
89
|
+
Dir.mktmpdir do |dir|
|
90
|
+
lockpath = File.join(dir, 'sample.lock')
|
91
|
+
parallel(2, lockpath) do
|
92
|
+
expect(File.exist?(lockpath)).to eq(true)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'runs in parallel without race condition' do
|
98
|
+
Dir.mktmpdir do |dir|
|
99
|
+
lockpath = File.join(dir, 'sample.lock')
|
100
|
+
|
101
|
+
answer = 0
|
102
|
+
|
103
|
+
begin
|
104
|
+
JustOneLock.filelock(lockpath) do
|
105
|
+
raise '42'
|
106
|
+
end
|
107
|
+
rescue RuntimeError
|
108
|
+
end
|
109
|
+
|
110
|
+
JustOneLock.filelock(lockpath) do
|
111
|
+
answer += 42
|
112
|
+
end
|
113
|
+
|
114
|
+
expect(answer).to eq(42)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'times out after specified number of seconds' do
|
119
|
+
Dir.mktmpdir do |dir|
|
120
|
+
lockpath = File.join(dir, 'sample.lock')
|
121
|
+
|
122
|
+
answer = 42
|
123
|
+
locked = false
|
124
|
+
|
125
|
+
Thread.new do
|
126
|
+
JustOneLock.filelock lockpath do
|
127
|
+
locked = true
|
128
|
+
sleep 20
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
Timeout::timeout(1) do
|
133
|
+
while locked == false
|
134
|
+
sleep 0.1
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
expect do
|
139
|
+
JustOneLock.filelock lockpath, timeout: 0.001 do
|
140
|
+
answer = 0
|
141
|
+
end
|
142
|
+
end.to raise_error(Timeout::Error)
|
143
|
+
|
144
|
+
expect(answer).to eq(42)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
# Java doesn't support forking
|
149
|
+
if RUBY_PLATFORM != 'java'
|
150
|
+
|
151
|
+
it 'should work for multiple processes' do
|
152
|
+
write('/tmp/number.txt', '0')
|
153
|
+
|
154
|
+
parallel_forks(6) do
|
155
|
+
number = File.read('/tmp/number.txt').to_i
|
156
|
+
sleep 0.3
|
157
|
+
write('/tmp/number.txt', (number + 7).to_s)
|
158
|
+
end
|
159
|
+
|
160
|
+
number = File.read('/tmp/number.txt').to_i
|
161
|
+
|
162
|
+
expect(number).to eq(42)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should handle heavy forking' do
|
166
|
+
write('/tmp/number.txt', '0')
|
167
|
+
|
168
|
+
parallel_forks(100) do
|
169
|
+
number = File.read('/tmp/number.txt').to_i
|
170
|
+
sleep 0.001
|
171
|
+
write('/tmp/number.txt', (number + 1).to_s)
|
172
|
+
end
|
173
|
+
|
174
|
+
number = File.read('/tmp/number.txt').to_i
|
175
|
+
|
176
|
+
expect(number).to eq(100)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should unblock files when killing processes' do
|
180
|
+
Dir.mktmpdir do |dir|
|
181
|
+
lockpath = File.join(dir, 'sample.lock')
|
182
|
+
|
183
|
+
Dir.mktmpdir do |dir|
|
184
|
+
pid = fork {
|
185
|
+
JustOneLock.filelock lockpath do
|
186
|
+
sleep 10
|
187
|
+
end
|
188
|
+
}
|
189
|
+
|
190
|
+
sleep 0.5
|
191
|
+
|
192
|
+
answer = 0
|
193
|
+
|
194
|
+
thread = Thread.new {
|
195
|
+
JustOneLock.filelock lockpath do
|
196
|
+
answer += 42
|
197
|
+
end
|
198
|
+
}
|
199
|
+
|
200
|
+
sleep 0.5
|
201
|
+
|
202
|
+
expect(answer).to eq(0)
|
203
|
+
Process.kill(9, pid)
|
204
|
+
thread.join
|
205
|
+
|
206
|
+
expect(answer).to eq(42)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should handle Pathname as well as string path' do
|
212
|
+
Dir.mktmpdir do |dir|
|
213
|
+
lockpath = Pathname.new(File.join(dir, 'sample.lock'))
|
214
|
+
|
215
|
+
answer = 0
|
216
|
+
JustOneLock.filelock lockpath do
|
217
|
+
answer += 42
|
218
|
+
end
|
219
|
+
|
220
|
+
expect(answer).to eq(42)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
|
226
|
+
# It failed for 1.8.7 (cannot convert to String)
|
227
|
+
it 'works for Tempfile' do
|
228
|
+
answer = 0
|
229
|
+
|
230
|
+
JustOneLock.filelock Tempfile.new(['sample', '.lock']) do
|
231
|
+
answer += 42
|
232
|
+
end
|
233
|
+
|
234
|
+
expect(answer).to eq(42)
|
235
|
+
end
|
236
|
+
|
237
|
+
# If implemented the wrong way lock is created elsewhere
|
238
|
+
it 'creates file with exact path provided' do
|
239
|
+
filename = "/tmp/awesome-lock-#{rand}.lock"
|
240
|
+
|
241
|
+
JustOneLock.filelock filename do
|
242
|
+
end
|
243
|
+
|
244
|
+
expect(File.exist?(filename)).to eq(true)
|
245
|
+
end
|
246
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: just_one_lock
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Stankiewicz, Yury Kotov
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: Simple solution to prevent multiple executions using flock
|
56
|
+
email:
|
57
|
+
- bairkan@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".bundle/config"
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rspec"
|
65
|
+
- Gemfile
|
66
|
+
- Gemfile.lock
|
67
|
+
- LICENSE
|
68
|
+
- README.md
|
69
|
+
- Rakefile
|
70
|
+
- just_one_lock.gemspec
|
71
|
+
- lib/just_one_lock.rb
|
72
|
+
- lib/just_one_lock/version.rb
|
73
|
+
- spec/just_one_lock_spec.rb
|
74
|
+
homepage: http://github.com/beorc/just_one_lock
|
75
|
+
licenses:
|
76
|
+
- MIT
|
77
|
+
metadata: {}
|
78
|
+
post_install_message:
|
79
|
+
rdoc_options: []
|
80
|
+
require_paths:
|
81
|
+
- lib
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - ">="
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
requirements: []
|
93
|
+
rubyforge_project:
|
94
|
+
rubygems_version: 2.2.2
|
95
|
+
signing_key:
|
96
|
+
specification_version: 4
|
97
|
+
summary: Simple solution to prevent multiple executions using flock
|
98
|
+
test_files:
|
99
|
+
- spec/just_one_lock_spec.rb
|