proco 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.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +346 -0
- data/Rakefile +26 -0
- data/benchmark/comparison.rb +103 -0
- data/benchmark/submission.rb +150 -0
- data/lib/proco.rb +112 -0
- data/lib/proco/dispatcher.rb +59 -0
- data/lib/proco/future.rb +40 -0
- data/lib/proco/logger.rb +21 -0
- data/lib/proco/mt/base.rb +65 -0
- data/lib/proco/mt/pool.rb +53 -0
- data/lib/proco/mt/threaded.rb +52 -0
- data/lib/proco/mt/worker.rb +81 -0
- data/lib/proco/queue/base.rb +58 -0
- data/lib/proco/queue/batch_queue.rb +30 -0
- data/lib/proco/queue/multi_queue.rb +30 -0
- data/lib/proco/queue/single_queue.rb +23 -0
- data/lib/proco/version.rb +3 -0
- data/proco.gemspec +24 -0
- data/test/test_mt_base.rb +40 -0
- data/test/test_mt_threaded.rb +32 -0
- data/test/test_pool.rb +20 -0
- data/test/test_proco.rb +197 -0
- data/test/test_queue.rb +105 -0
- data/test/test_worker.rb +19 -0
- metadata +144 -0
data/test/test_proco.rb
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
$VERBOSE = true
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) if $0 == __FILE__
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'minitest/autorun'
|
|
5
|
+
require 'proco'
|
|
6
|
+
require 'parallelize'
|
|
7
|
+
require 'logger'
|
|
8
|
+
|
|
9
|
+
describe Proco do
|
|
10
|
+
before do
|
|
11
|
+
@logger = Logger.new($stdout)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "can be created with method chaining" do
|
|
15
|
+
proco = Proco.interval(1).threads(4).queues(4).queue_size(100).batch(true).logger(@logger).new
|
|
16
|
+
opts = proco.options
|
|
17
|
+
|
|
18
|
+
assert_equal 1, opts[:interval]
|
|
19
|
+
assert_equal 4, opts[:threads]
|
|
20
|
+
assert_equal 4, opts[:queues]
|
|
21
|
+
assert_equal 100, opts[:queue_size]
|
|
22
|
+
assert_equal true, opts[:batch]
|
|
23
|
+
assert_equal @logger, opts[:logger]
|
|
24
|
+
|
|
25
|
+
proco = Proco.threads(8).interval(10).new(:interval => 20)
|
|
26
|
+
opts = proco.options
|
|
27
|
+
assert_equal 20, opts[:interval]
|
|
28
|
+
assert_equal 8, opts[:threads]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "in default setting" do
|
|
32
|
+
before do
|
|
33
|
+
@proco = Proco.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
it "yields each item" do
|
|
37
|
+
@proco.start do |items|
|
|
38
|
+
assert_equal false, items.is_a?(Array)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
@proco.submit! 1
|
|
42
|
+
@proco.submit 2
|
|
43
|
+
|
|
44
|
+
@proco.exit
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "acts as a FCFS queue" do
|
|
48
|
+
processed = []
|
|
49
|
+
@proco.start do |item|
|
|
50
|
+
processed << item
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
feed = []
|
|
54
|
+
100.times do |i|
|
|
55
|
+
feed << i
|
|
56
|
+
@proco.submit! i
|
|
57
|
+
end
|
|
58
|
+
@proco.exit
|
|
59
|
+
|
|
60
|
+
assert_equal feed, processed
|
|
61
|
+
assert_equal 100, processed.length
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
describe "in batch-mode with multiple queues" do
|
|
66
|
+
before do
|
|
67
|
+
@proco = Proco.queue_size(10).queues(2).threads(4).batch(true).logger(@logger).new
|
|
68
|
+
@mutex = Mutex.new
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "always yields Array" do
|
|
72
|
+
bool = true
|
|
73
|
+
@proco.start do |items|
|
|
74
|
+
assert items.is_a?(Array)
|
|
75
|
+
end
|
|
76
|
+
@proco.submit 1
|
|
77
|
+
@proco.submit 2
|
|
78
|
+
@proco.submit! 3
|
|
79
|
+
|
|
80
|
+
@proco.exit
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "handles synchronous requests" do
|
|
84
|
+
cnt = 0
|
|
85
|
+
@proco.start do |items|
|
|
86
|
+
@mutex.synchronize do
|
|
87
|
+
cnt += items.inject(:+)
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
1000.times { |i| @proco.submit i }
|
|
91
|
+
assert_equal 1000.times.inject(:+), cnt
|
|
92
|
+
|
|
93
|
+
@proco.exit
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "handles asynchronous requests" do
|
|
97
|
+
cnt = 0
|
|
98
|
+
@proco.start do |items|
|
|
99
|
+
@mutex.synchronize do
|
|
100
|
+
cnt += items.inject(:+)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
1000.times { |i| @proco.submit! i }
|
|
104
|
+
@proco.exit
|
|
105
|
+
assert_equal 1000.times.inject(:+), cnt
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
describe "in batch mode" do
|
|
110
|
+
it "yields batch_size items if set" do
|
|
111
|
+
proco = Proco.interval(0.1).batch_size(10).queue_size(1000).batch(true).new
|
|
112
|
+
cnt = 0
|
|
113
|
+
proco.start do |items|
|
|
114
|
+
# FIXME: naive
|
|
115
|
+
assert_equal 10, items.length
|
|
116
|
+
cnt += 1
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
100.times do |i|
|
|
120
|
+
proco.submit! i
|
|
121
|
+
end
|
|
122
|
+
proco.exit
|
|
123
|
+
assert_equal 10, cnt
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "works as follows" do
|
|
127
|
+
proco = Proco.interval(0.1).threads(4).queues(8).queue_size(10).batch(true).new
|
|
128
|
+
|
|
129
|
+
assert_equal false, proco.running?
|
|
130
|
+
|
|
131
|
+
# No you can't submit an item yet
|
|
132
|
+
assert_raises(RuntimeError) { proco.submit :not_yet }
|
|
133
|
+
assert_raises(RuntimeError) { proco.exit }
|
|
134
|
+
assert_raises(RuntimeError) { proco.kill }
|
|
135
|
+
|
|
136
|
+
assert_raises(ArgumentError) {
|
|
137
|
+
proco.start
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
proco.start do |batch|
|
|
141
|
+
# Batch-process items every 0.1 seconds
|
|
142
|
+
# ...
|
|
143
|
+
# puts "#{Thread.current}: #{batch}"
|
|
144
|
+
batch.length
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
assert_equal true, proco.running?
|
|
148
|
+
|
|
149
|
+
# Synchronous submit
|
|
150
|
+
50.times do |i|
|
|
151
|
+
result = proco.submit i
|
|
152
|
+
assert_instance_of Fixnum, result
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Asynchronous submit
|
|
156
|
+
futures = 50.times.map { |i|
|
|
157
|
+
proco.submit! i
|
|
158
|
+
}
|
|
159
|
+
futures.each do |future|
|
|
160
|
+
assert_instance_of Proco::Future, future
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# Wait until the batch containing the item is processed
|
|
164
|
+
assert_equal 50, futures.uniq.map { |f| f.get }.inject(:+)
|
|
165
|
+
|
|
166
|
+
proco.exit
|
|
167
|
+
assert_equal false, proco.running?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "can be killed without waiting" do
|
|
173
|
+
proco = Proco.batch(true).new
|
|
174
|
+
cnt = 0
|
|
175
|
+
proco.start do |items|
|
|
176
|
+
sleep 1
|
|
177
|
+
cnt += items.length
|
|
178
|
+
end
|
|
179
|
+
100.times do |i|
|
|
180
|
+
proco.submit! i
|
|
181
|
+
end
|
|
182
|
+
proco.exit
|
|
183
|
+
assert_equal 100, cnt
|
|
184
|
+
|
|
185
|
+
cnt = 0
|
|
186
|
+
proco.start do |items|
|
|
187
|
+
sleep 1
|
|
188
|
+
cnt += items.length
|
|
189
|
+
end
|
|
190
|
+
100.times do |i|
|
|
191
|
+
proco.submit! i
|
|
192
|
+
end
|
|
193
|
+
proco.kill
|
|
194
|
+
assert_equal 0, cnt
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
data/test/test_queue.rb
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
$VERBOSE = true
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) if $0 == __FILE__
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'minitest/autorun'
|
|
5
|
+
require 'proco'
|
|
6
|
+
require 'lps'
|
|
7
|
+
|
|
8
|
+
class TestQueue < MiniTest::Unit::TestCase
|
|
9
|
+
def test_single_queue
|
|
10
|
+
q = Proco::Queue::SingleQueue.new 100
|
|
11
|
+
f = q.push 1
|
|
12
|
+
assert_instance_of Proco::Future, f
|
|
13
|
+
q.push 2
|
|
14
|
+
|
|
15
|
+
f2, i = q.take
|
|
16
|
+
assert_equal f, f2
|
|
17
|
+
assert_equal 1, i
|
|
18
|
+
|
|
19
|
+
f3, i = q.take
|
|
20
|
+
assert_equal 2, i
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_batch_queue
|
|
24
|
+
q = Proco::Queue::BatchQueue.new 100, 10
|
|
25
|
+
|
|
26
|
+
futures = 10.times.map { |i| q.push i }
|
|
27
|
+
assert_equal 1, futures.uniq.length
|
|
28
|
+
|
|
29
|
+
future = futures.first
|
|
30
|
+
future2 = q.push :next
|
|
31
|
+
assert future != future2
|
|
32
|
+
|
|
33
|
+
f, items = q.take
|
|
34
|
+
assert_equal future, f
|
|
35
|
+
assert_equal 10.times.to_a, items
|
|
36
|
+
|
|
37
|
+
f, items = q.take
|
|
38
|
+
assert_equal future2, f
|
|
39
|
+
assert_equal [:next], items
|
|
40
|
+
|
|
41
|
+
f = q.push :next
|
|
42
|
+
assert future2 != f
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def test_multi_queue
|
|
46
|
+
q = Proco::Queue::MultiQueue.new 100
|
|
47
|
+
f1 = q.push 1
|
|
48
|
+
f2 = q.push 2
|
|
49
|
+
f3 = q.push 3
|
|
50
|
+
assert_equal f1, f2
|
|
51
|
+
assert_equal f2, f3
|
|
52
|
+
|
|
53
|
+
f4, items = q.take
|
|
54
|
+
assert_equal f3, f4
|
|
55
|
+
assert_equal [1, 2, 3], items
|
|
56
|
+
|
|
57
|
+
f5 = q.push 4
|
|
58
|
+
assert ! f4 != f5
|
|
59
|
+
|
|
60
|
+
f6, items = q.take
|
|
61
|
+
assert_equal f5, f6
|
|
62
|
+
assert_equal [4], items
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def test_multi_queue_complex
|
|
66
|
+
queue = Proco::Queue::MultiQueue.new(200)
|
|
67
|
+
futures = []
|
|
68
|
+
num_batches = 0
|
|
69
|
+
num_items = 0
|
|
70
|
+
|
|
71
|
+
t1 = Thread.new {
|
|
72
|
+
1000.times do |i|
|
|
73
|
+
futures << queue.push(i)
|
|
74
|
+
end
|
|
75
|
+
queue.invalidate
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
t2 = Thread.new {
|
|
79
|
+
future = items = nil
|
|
80
|
+
begin
|
|
81
|
+
LPS.freq(10).while {
|
|
82
|
+
future, items = queue.take
|
|
83
|
+
future
|
|
84
|
+
}.loop do
|
|
85
|
+
num_batches += 1
|
|
86
|
+
num_items += items.length
|
|
87
|
+
|
|
88
|
+
future.update do
|
|
89
|
+
items.length
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
rescue Exception => e
|
|
93
|
+
puts e
|
|
94
|
+
end
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
t1.join
|
|
98
|
+
t2.join
|
|
99
|
+
|
|
100
|
+
assert_equal 1000, futures.length
|
|
101
|
+
assert_equal num_batches, futures.uniq.length
|
|
102
|
+
assert_equal 1000, num_items
|
|
103
|
+
assert_equal 1000, futures.uniq.map { |future| future.get }.inject(:+)
|
|
104
|
+
end
|
|
105
|
+
end
|
data/test/test_worker.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$VERBOSE = true
|
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__) if $0 == __FILE__
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'minitest/autorun'
|
|
5
|
+
require 'proco'
|
|
6
|
+
|
|
7
|
+
class TestWorker < MiniTest::Unit::TestCase
|
|
8
|
+
def test_worker
|
|
9
|
+
w = Proco::MT::Worker.new nil
|
|
10
|
+
cnt = 0
|
|
11
|
+
w.assign { sleep 0.1; cnt += 1 }
|
|
12
|
+
w.assign { sleep 0.1; cnt += 1 }
|
|
13
|
+
w.assign { sleep 0.1; cnt += 1 } # Async
|
|
14
|
+
assert_equal 1, cnt
|
|
15
|
+
sleep 0.2;
|
|
16
|
+
w.exit
|
|
17
|
+
assert_equal 3, cnt
|
|
18
|
+
end
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: proco
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Junegunn Choi
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2013-03-04 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: lps
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ~>
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: 0.1.3
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ~>
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: 0.1.3
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: option_initializer
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ~>
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: 1.2.0
|
|
38
|
+
type: :runtime
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ~>
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: 1.2.0
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: minitest
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
- !ruby/object:Gem::Dependency
|
|
63
|
+
name: parallelize
|
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
|
65
|
+
none: false
|
|
66
|
+
requirements:
|
|
67
|
+
- - ! '>='
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
type: :development
|
|
71
|
+
prerelease: false
|
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
+
none: false
|
|
74
|
+
requirements:
|
|
75
|
+
- - ! '>='
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: '0'
|
|
78
|
+
description: A lightweight asynchronous task executor service designed for efficient
|
|
79
|
+
batch processing
|
|
80
|
+
email:
|
|
81
|
+
- junegunn.c@gmail.com
|
|
82
|
+
executables: []
|
|
83
|
+
extensions: []
|
|
84
|
+
extra_rdoc_files: []
|
|
85
|
+
files:
|
|
86
|
+
- .gitignore
|
|
87
|
+
- Gemfile
|
|
88
|
+
- LICENSE.txt
|
|
89
|
+
- README.md
|
|
90
|
+
- Rakefile
|
|
91
|
+
- benchmark/comparison.rb
|
|
92
|
+
- benchmark/submission.rb
|
|
93
|
+
- lib/proco.rb
|
|
94
|
+
- lib/proco/dispatcher.rb
|
|
95
|
+
- lib/proco/future.rb
|
|
96
|
+
- lib/proco/logger.rb
|
|
97
|
+
- lib/proco/mt/base.rb
|
|
98
|
+
- lib/proco/mt/pool.rb
|
|
99
|
+
- lib/proco/mt/threaded.rb
|
|
100
|
+
- lib/proco/mt/worker.rb
|
|
101
|
+
- lib/proco/queue/base.rb
|
|
102
|
+
- lib/proco/queue/batch_queue.rb
|
|
103
|
+
- lib/proco/queue/multi_queue.rb
|
|
104
|
+
- lib/proco/queue/single_queue.rb
|
|
105
|
+
- lib/proco/version.rb
|
|
106
|
+
- proco.gemspec
|
|
107
|
+
- test/test_mt_base.rb
|
|
108
|
+
- test/test_mt_threaded.rb
|
|
109
|
+
- test/test_pool.rb
|
|
110
|
+
- test/test_proco.rb
|
|
111
|
+
- test/test_queue.rb
|
|
112
|
+
- test/test_worker.rb
|
|
113
|
+
homepage: https://github.com/junegunn/proco
|
|
114
|
+
licenses: []
|
|
115
|
+
post_install_message:
|
|
116
|
+
rdoc_options: []
|
|
117
|
+
require_paths:
|
|
118
|
+
- lib
|
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
|
+
none: false
|
|
121
|
+
requirements:
|
|
122
|
+
- - ! '>='
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
126
|
+
none: false
|
|
127
|
+
requirements:
|
|
128
|
+
- - ! '>='
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '0'
|
|
131
|
+
requirements: []
|
|
132
|
+
rubyforge_project:
|
|
133
|
+
rubygems_version: 1.8.25
|
|
134
|
+
signing_key:
|
|
135
|
+
specification_version: 3
|
|
136
|
+
summary: A lightweight asynchronous task executor service designed for efficient batch
|
|
137
|
+
processing
|
|
138
|
+
test_files:
|
|
139
|
+
- test/test_mt_base.rb
|
|
140
|
+
- test/test_mt_threaded.rb
|
|
141
|
+
- test/test_pool.rb
|
|
142
|
+
- test/test_proco.rb
|
|
143
|
+
- test/test_queue.rb
|
|
144
|
+
- test/test_worker.rb
|