just_one_lock 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd505c4500de6a9d5ac9fca007c2d204bfe2f6a5
4
- data.tar.gz: 2817838753a95afe43173d5e867adfb8f9006d8d
3
+ metadata.gz: 4555943b102ddd4bac3f2c732d54194126999e29
4
+ data.tar.gz: 56f6671b415251bb76ee48aa37b68155dcf2bfc4
5
5
  SHA512:
6
- metadata.gz: 661c30672498de83b0380075cb0629e9d003ae132c41f906b3a90a171adfb6a0037f95f3fed7720a21fb16a08cddd545701cd26d4a6f4d7aee9046bffbb57439
7
- data.tar.gz: ccc5ae6dfde9a056f54d551f2084c7d6796318611ad01ee9c48fc3058ba8decdf4f750c2362128b57829df75ca36b8eec3877d903bb89990baaa066a0ea5f219
6
+ metadata.gz: 991fdcbbc3634896fb0e83a68f51fe7ffa24c90b124894a3977b5ee6779d5b76efdb3ebbd2dcce7f06a4e5a27c178ce8b973940cad8aadbf824806d6716921ab
7
+ data.tar.gz: 77d27fdd32f70e77a11b3ff915d9b3ee8fcd064505fac4ab7a968d9829529825b000bc6352788aeefc53956edff10f77c8ea038e89eac3e36f399d6022427c82
data/Gemfile.lock CHANGED
@@ -27,5 +27,5 @@ PLATFORMS
27
27
  DEPENDENCIES
28
28
  bundler (~> 1.3)
29
29
  just_one_lock!
30
- rake
31
- rspec
30
+ rake (~> 10.3)
31
+ rspec (~> 3.0)
data/lib/just_one_lock.rb CHANGED
@@ -2,22 +2,32 @@ require 'just_one_lock/version'
2
2
  require 'timeout'
3
3
 
4
4
  module JustOneLock
5
- def self.filelock(lockname, options = {}, &block)
5
+ DEFAULT_TIMEOUT = 0.01
6
+ class AlreadyLocked < StandardError; end
7
+
8
+ class NullStream
9
+ class << self
10
+ def puts(str); end
11
+ def <<(o); self; end
12
+ end
13
+ end
14
+
15
+ def self.filelock(lockname, timeout: JustOneLock::DEFAULT_TIMEOUT, &block)
6
16
  File.open(lockname, File::RDWR|File::CREAT, 0644) do |file|
7
- Timeout::timeout(options.fetch(:timeout, 60)) { file.flock(File::LOCK_EX) }
17
+ Timeout::timeout(timeout, JustOneLock::AlreadyLocked) { file.flock(File::LOCK_EX) }
18
+
8
19
  yield
9
20
  end
10
21
  end
11
22
 
12
- def self.prevent_multiple_executions(lock_dir, scope, &block)
23
+ def self.prevent_multiple_executions(lock_dir, scope, output: JustOneLock::NullStream, timeout: JustOneLock::DEFAULT_TIMEOUT, &block)
13
24
  scope_name = scope.gsub(':', '_')
14
25
  lock_path = File.join(lock_dir, scope_name + '.lock')
15
26
 
16
27
  begin
17
- return filelock(lock_path, &block)
18
- rescue Timeout::Error => e
19
- puts "Another process <#{scope}> already is running"
20
- exit(1)
28
+ return filelock(lock_path, timeout: timeout, &block)
29
+ rescue JustOneLock::AlreadyLocked => e
30
+ output.puts "Another process <#{scope}> already is running"
21
31
  end
22
32
  end
23
33
  end
@@ -1,3 +1,3 @@
1
1
  module JustOneLock
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -9,25 +9,32 @@ describe JustOneLock do
9
9
  File.open(filename.to_s, 'w') { |f| f.write(contents) }
10
10
  end
11
11
 
12
- def parallel(n = 2, templock = nil, &block)
13
- Timeout::timeout(5) do
14
- templock ||= Tempfile.new(['sample', '.lock'])
12
+ def dir_and_scope(lockpath)
13
+ path_segments = lockpath.split('/')
14
+ scope = path_segments.last
15
+ path_segments.delete scope
16
+ dir = path_segments.join('/')
17
+ [dir, scope]
18
+ end
15
19
 
20
+ def parallel(n = 2, lockpath: Tempfile.new(['sample', '.lock']).path, timeout: JustOneLock::DEFAULT_TIMEOUT, &block)
21
+ Timeout::timeout(5) do
22
+ dir, scope = dir_and_scope(lockpath)
16
23
  (1..n).map do
17
24
  Thread.new do
18
- JustOneLock.filelock(templock, &block)
25
+ JustOneLock.prevent_multiple_executions(dir, scope, timeout: timeout, &block)
19
26
  end
20
27
  end.map(&:join)
21
28
  end
22
29
  end
23
30
 
24
- def parallel_forks(n = 2, templock = nil, &block)
31
+ def parallel_forks(n = 2, lockpath: Tempfile.new(['sample', '.lock']).path, timeout: JustOneLock::DEFAULT_TIMEOUT, &block)
25
32
  Timeout::timeout(5) do
26
- templock ||= Tempfile.new(['sample', '.lock'])
33
+ dir, scope = dir_and_scope(lockpath)
27
34
 
28
35
  (1..n).map do
29
36
  fork {
30
- JustOneLock.filelock(templock, &block)
37
+ JustOneLock.prevent_multiple_executions(dir, scope, timeout: timeout, &block)
31
38
  }
32
39
  end.map do |pid|
33
40
  Process.waitpid(pid)
@@ -66,19 +73,18 @@ describe JustOneLock do
66
73
 
67
74
  parallel(2) do
68
75
  value = answer
69
- sleep 0.1
76
+ sleep(JustOneLock::DEFAULT_TIMEOUT / 2)
70
77
  answer = value + 21
71
78
  end
72
79
 
73
80
  expect(answer).to eq(42)
74
81
  end
75
82
 
76
- it 'handels high amount of concurrent tasks' do
83
+ it 'handles high amount of concurrent tasks' do
77
84
  answer = 0
78
85
 
79
86
  parallel(100) do
80
87
  value = answer
81
- sleep 0.001
82
88
  answer = value + 1
83
89
  end
84
90
 
@@ -86,33 +92,29 @@ describe JustOneLock do
86
92
  end
87
93
 
88
94
  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
95
+ lockpath = Tempfile.new(['sample', '.lock']).path
96
+ parallel(2, lockpath: lockpath) do
97
+ expect(File.exist?(lockpath)).to eq(true)
94
98
  end
95
99
  end
96
100
 
97
101
  it 'runs in parallel without race condition' do
98
- Dir.mktmpdir do |dir|
99
- lockpath = File.join(dir, 'sample.lock')
102
+ lockpath = Tempfile.new(['sample', '.lock']).path
100
103
 
101
- answer = 0
102
-
103
- begin
104
- JustOneLock.filelock(lockpath) do
105
- raise '42'
106
- end
107
- rescue RuntimeError
108
- end
104
+ answer = 0
109
105
 
106
+ begin
110
107
  JustOneLock.filelock(lockpath) do
111
- answer += 42
108
+ raise '42'
112
109
  end
110
+ rescue RuntimeError
111
+ end
113
112
 
114
- expect(answer).to eq(42)
113
+ JustOneLock.filelock(lockpath) do
114
+ answer += 42
115
115
  end
116
+
117
+ expect(answer).to eq(42)
116
118
  end
117
119
 
118
120
  it 'times out after specified number of seconds' do
@@ -139,7 +141,7 @@ describe JustOneLock do
139
141
  JustOneLock.filelock lockpath, timeout: 0.001 do
140
142
  answer = 0
141
143
  end
142
- end.to raise_error(Timeout::Error)
144
+ end.to raise_error(JustOneLock::AlreadyLocked)
143
145
 
144
146
  expect(answer).to eq(42)
145
147
  end
@@ -153,7 +155,7 @@ describe JustOneLock do
153
155
 
154
156
  parallel_forks(6) do
155
157
  number = File.read('/tmp/number.txt').to_i
156
- sleep 0.3
158
+ sleep(JustOneLock::DEFAULT_TIMEOUT / 100)
157
159
  write('/tmp/number.txt', (number + 7).to_s)
158
160
  end
159
161
 
@@ -165,46 +167,47 @@ describe JustOneLock do
165
167
  it 'should handle heavy forking' do
166
168
  write('/tmp/number.txt', '0')
167
169
 
168
- parallel_forks(100) do
170
+ FORKS_NUMBER = 100
171
+ parallel_forks(FORKS_NUMBER, timeout: 1) do
169
172
  number = File.read('/tmp/number.txt').to_i
170
- sleep 0.001
171
173
  write('/tmp/number.txt', (number + 1).to_s)
172
174
  end
173
175
 
174
176
  number = File.read('/tmp/number.txt').to_i
175
177
 
176
- expect(number).to eq(100)
178
+ expect(number).to eq(FORKS_NUMBER)
177
179
  end
178
180
 
179
181
  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
- }
182
+ lockpath = Tempfile.new(['sample', '.lock']).path
183
+ dir, scope = dir_and_scope(lockpath)
189
184
 
190
- sleep 0.5
185
+ Dir.mktmpdir do |dir|
186
+ pid = fork {
187
+ JustOneLock.prevent_multiple_executions(dir, scope) do
188
+ sleep 10
189
+ end
190
+ }
191
191
 
192
- answer = 0
192
+ Timeout::timeout(1) do
193
+ while !File.exist?(lockpath)
194
+ sleep 0.1
195
+ end
196
+ end
193
197
 
194
- thread = Thread.new {
195
- JustOneLock.filelock lockpath do
196
- answer += 42
197
- end
198
- }
198
+ answer = 0
199
199
 
200
- sleep 0.5
200
+ thread = Thread.new {
201
+ JustOneLock.prevent_multiple_executions(dir, scope) do
202
+ answer += 42
203
+ end
204
+ }
201
205
 
202
- expect(answer).to eq(0)
203
- Process.kill(9, pid)
204
- thread.join
206
+ expect(answer).to eq(0)
207
+ Process.kill(9, pid)
208
+ thread.join
205
209
 
206
- expect(answer).to eq(42)
207
- end
210
+ expect(answer).to eq(42)
208
211
  end
209
212
  end
210
213
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: just_one_lock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Stankiewicz, Yury Kotov