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 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
@@ -0,0 +1,3 @@
1
+ ---
2
+ BUNDLE_PATH: "../gems"
3
+ BUNDLE_DISABLE_SHARED_GEMS: '1'
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.swp
2
+ tags
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in filelock.gemspec
4
+ gemspec
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
@@ -0,0 +1,4 @@
1
+ just_one_lock
2
+ =============
3
+
4
+ Simple solution to prevent multiple executions using flock
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,3 @@
1
+ module JustOneLock
2
+ VERSION = "0.0.1"
3
+ 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