perfectqueue 0.8.47 → 0.8.48
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +3 -4
- data/ChangeLog +8 -0
- data/README.md +9 -1
- data/bin/stress +201 -0
- data/lib/perfectqueue/backend/rdb_compat.rb +13 -4
- data/lib/perfectqueue/version.rb +1 -1
- data/perfectqueue.gemspec +1 -0
- data/spec/supervisor_spec.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 56f251e9261948702f9f058179343788c08ea08e
|
4
|
+
data.tar.gz: 4bd8336b27bfc2097a22581120e872fe621d4c53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a826da88811b6f511b4e4b0a6bdab9255a0a792848874b761fb9c2b6014fb3ac32f1c2a8231c8bdd54dfec82bb37fbd46b87664d392f829fb9fadfcb4388d3e
|
7
|
+
data.tar.gz: 4591e5a7f253502dafb1687ce57d572fa8b7742b2482be6938d41ce501d9e42f44cc4a2ca3e9a13a4a7de6f50f3518831b2bc9ba60bbac5ec29930ea67c36602
|
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
== 2016-07-07 version 0.8.48
|
2
|
+
|
3
|
+
* Drop 2.0.0 support #33
|
4
|
+
* Show taken time of delete and acquire (#33)
|
5
|
+
* To ease the migration delete v0.8.44 finished tasks too (#34)
|
6
|
+
* Add stress test tool (#35)
|
7
|
+
* Fix cleanup interval count (#36)
|
8
|
+
|
1
9
|
== 2016-04-15 version 0.8.47
|
2
10
|
|
3
11
|
* require libraries before fork to help CoW
|
data/README.md
CHANGED
@@ -189,6 +189,14 @@ additional configuration:
|
|
189
189
|
|
190
190
|
Not implemented yet.
|
191
191
|
|
192
|
+
## config/perfectqueue.yml Example
|
193
|
+
|
194
|
+
```
|
195
|
+
development:
|
196
|
+
type: rdb_compat
|
197
|
+
url: mysql2://root:@localhost:3306/perfectqueue
|
198
|
+
table: queues
|
199
|
+
```
|
192
200
|
|
193
201
|
## Command line management tool
|
194
202
|
|
@@ -241,7 +249,7 @@ options for run:
|
|
241
249
|
|
242
250
|
### force finish a tasks
|
243
251
|
|
244
|
-
$ perfectqueue
|
252
|
+
$ perfectqueue force_finish k2
|
245
253
|
|
246
254
|
### running a worker
|
247
255
|
|
data/bin/stress
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
3
|
+
require 'sequel'
|
4
|
+
require 'perfectqueue'
|
5
|
+
require 'perfectqueue/backend/rdb_compat'
|
6
|
+
require 'securerandom'
|
7
|
+
require 'optparse'
|
8
|
+
|
9
|
+
queue_config =
|
10
|
+
{
|
11
|
+
:type => 'rdb_compat',
|
12
|
+
:url => "mysql2://testuser:@localhost:3306/perfectqueue_test",
|
13
|
+
:table => 'test_stress',
|
14
|
+
:disable_resource_limit => true, # TODO backend-specific test cases
|
15
|
+
:cleanup_interval => 200,
|
16
|
+
insert_processes: 0,
|
17
|
+
worker_processes: 0,
|
18
|
+
}
|
19
|
+
opt = OptionParser.new
|
20
|
+
|
21
|
+
opt.on('--url URL', 'database url') {|v| queue_config[:url] = v }
|
22
|
+
opt.on('--table TABLE', 'table name') {|v| queue_config[:table] = v }
|
23
|
+
opt.on('--disable_resource_limit=true', TrueClass, 'use resource limit or not') {|v| queue_config[:disable_resource_limit] = v }
|
24
|
+
opt.on('--cleanup_interval SECOND', Integer, 'cleanup interval') {|v| queue_config[:cleanup_interval] = v }
|
25
|
+
opt.on('--insert_processes NUM', Integer, 'inserts') {|v| queue_config[:insert_processes] = v }
|
26
|
+
opt.on('--worker_processes NUM', Integer, 'workers') {|v| queue_config[:worker_processes] = v }
|
27
|
+
opt.on('--retention-time SECOND', Integer, 'retention time') {|v| queue_config[:retention_time] = v }
|
28
|
+
opt.parse!(ARGV)
|
29
|
+
|
30
|
+
module PerfectQueue
|
31
|
+
class Queue
|
32
|
+
def submit1000(data)
|
33
|
+
@client.submit1000(data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
class Client
|
37
|
+
def submit1000(data)
|
38
|
+
@backend.submit1000(data)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
module Backend
|
42
|
+
class RDBCompatBackend
|
43
|
+
def submit1000(h)
|
44
|
+
rd = Random.new
|
45
|
+
i = 0
|
46
|
+
connect {
|
47
|
+
begin
|
48
|
+
begin
|
49
|
+
n = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
50
|
+
submit0("import.1/main.action_#{n}_#{rd.hex(80)}", 'user02', h, now: n)
|
51
|
+
end while (i+=1) < 10000
|
52
|
+
end
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
# => Task
|
57
|
+
def submit0(key, type, data, options)
|
58
|
+
now = (options[:now] || Time.now).to_i
|
59
|
+
now = 1 if now < 1 # 0 means cancel requested
|
60
|
+
run_at = (options[:run_at] || now).to_i
|
61
|
+
user = options[:user]
|
62
|
+
user = user.to_s if user
|
63
|
+
max_running = options[:max_running]
|
64
|
+
data = data ? data.dup : {}
|
65
|
+
data['type'] = type
|
66
|
+
|
67
|
+
d = data.to_json
|
68
|
+
|
69
|
+
if options[:compression] == 'gzip'
|
70
|
+
require 'zlib'
|
71
|
+
require 'stringio'
|
72
|
+
io = StringIO.new
|
73
|
+
gz = Zlib::GzipWriter.new(io)
|
74
|
+
begin
|
75
|
+
gz.write(d)
|
76
|
+
ensure
|
77
|
+
gz.close
|
78
|
+
end
|
79
|
+
d = io.string
|
80
|
+
d.force_encoding('ASCII-8BIT') if d.respond_to?(:force_encoding)
|
81
|
+
d = Sequel::SQL::Blob.new(d)
|
82
|
+
end
|
83
|
+
|
84
|
+
@db[
|
85
|
+
"INSERT INTO `#{@table}` (id, timeout, data, created_at, resource, max_running) VALUES (?, ?, ?, ?, ?, ?)",
|
86
|
+
key, run_at, d, now, user, max_running
|
87
|
+
].insert
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
def insert1000(queue)
|
95
|
+
Process.setproctitle'bin/stress/insert'
|
96
|
+
while 1
|
97
|
+
t0 = t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
98
|
+
h = {"path":"in/1/main.action.e9f070b5bfea96442af13ce6acc36699_0f7ad8aee859867aae303190e372ec1e.msgpack.gz",
|
99
|
+
"size":11373,
|
100
|
+
"format":"msgpack.gz",
|
101
|
+
"account_id":1,
|
102
|
+
"start_at":nil,
|
103
|
+
"database":"main",
|
104
|
+
"table":"action",
|
105
|
+
"table_path":"action_20160516_d85364_e43b23f4",
|
106
|
+
"table_id":123456,
|
107
|
+
"user":1,
|
108
|
+
"c3769c43b07202bf6f456f4b99639a15":"1/main/action_20160516_d85364_e43b23f4",
|
109
|
+
"hogefuga":"e9f070b5bfea96442af13ce6acc36699",
|
110
|
+
"unique_id":"2d7325ef94e3eda8a20b65d33696d45a",
|
111
|
+
"hoge_callback":{"url":"f117c9682a9c5129275f39493599272f809fcf3064edbfacc1c0a5",
|
112
|
+
"headers":{"21a166dedd97bc":"TD1 4f8e44c0880086628ad7349e69d7d6ba19ce05ae"},
|
113
|
+
"params":{}},
|
114
|
+
"params":{}}
|
115
|
+
begin
|
116
|
+
queue.submit1000(h)
|
117
|
+
rescue Sequel::DatabaseError
|
118
|
+
p $!
|
119
|
+
sleep 5
|
120
|
+
end
|
121
|
+
t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
122
|
+
puts "#{__method__}#{Process.pid}: #{t-t0}sec for 1000 inserts\n"
|
123
|
+
end
|
124
|
+
rescue Interrupt
|
125
|
+
exit
|
126
|
+
end
|
127
|
+
|
128
|
+
def insert(queue)
|
129
|
+
while 1
|
130
|
+
rd = Random.new
|
131
|
+
i = 0
|
132
|
+
t0 = t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
133
|
+
h = {"path":"in/1/main.action.e9f070b5bfea96442af13ce6acc36699_0f7ad8aee859867aae303190e372ec1e.msgpack.gz",
|
134
|
+
"size":11373,
|
135
|
+
"format":"msgpack.gz",
|
136
|
+
"account_id":1,
|
137
|
+
"start_at":nil,
|
138
|
+
"database":"main",
|
139
|
+
"table":"action",
|
140
|
+
"table_path":"action_20160516_d85364_e43b23f4",
|
141
|
+
"table_id":123456,
|
142
|
+
"user":1,
|
143
|
+
"c3769c43b07202bf6f456f4b99639a15":"1/main/action_20160516_d85364_e43b23f4",
|
144
|
+
"hogefuga":"e9f070b5bfea96442af13ce6acc36699",
|
145
|
+
"unique_id":"2d7325ef94e3eda8a20b65d33696d45a",
|
146
|
+
"hoge_callback":{"url":"f117c9682a9c5129275f39493599272f809fcf3064edbfacc1c0a5",
|
147
|
+
"headers":{"21a166dedd97bc":"TD1 4f8e44c0880086628ad7349e69d7d6ba19ce05ae"},
|
148
|
+
"params":{}},
|
149
|
+
"params":{}}
|
150
|
+
begin
|
151
|
+
n = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
152
|
+
queue.submit("import.1/main.action_#{n}_#{rd.hex(80)}", 'user02', h, now: t)
|
153
|
+
rescue
|
154
|
+
p $!
|
155
|
+
sleep 1
|
156
|
+
end while (i+=1) < 1000
|
157
|
+
t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
158
|
+
puts "#{__method__}#{Process.pid}: #{t-t0}sec for 1000 inserts\n"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def worker(queue)
|
163
|
+
while 1
|
164
|
+
i = 0
|
165
|
+
t0 = t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
166
|
+
begin
|
167
|
+
ary = queue.poll_multi(max_acquire: 11, now: t)
|
168
|
+
ary.each do |x|
|
169
|
+
x.finish!({})
|
170
|
+
end if ary
|
171
|
+
t = Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
172
|
+
rescue
|
173
|
+
p $!.class
|
174
|
+
sleep 1
|
175
|
+
end while (i+=1) < 100
|
176
|
+
puts "#{__method__}#{Process.pid}: #{t-t0}sec for 100 acquires\n"
|
177
|
+
end
|
178
|
+
rescue Interrupt
|
179
|
+
exit
|
180
|
+
end
|
181
|
+
|
182
|
+
pids = []
|
183
|
+
queue = PerfectQueue.open(queue_config)
|
184
|
+
#queue.client.init_database(:force => true)
|
185
|
+
queue.config[:insert_processes].times do
|
186
|
+
pids << fork { insert1000(queue) }
|
187
|
+
end
|
188
|
+
queue.config[:worker_processes].times do
|
189
|
+
#queue.client.backend.instance_variable_set(:@cleanup_interval_count, rand(200))
|
190
|
+
pids << fork { worker(queue) }
|
191
|
+
end
|
192
|
+
queue.close
|
193
|
+
|
194
|
+
trap (:INT) do
|
195
|
+
pids.each do |pid|
|
196
|
+
Process.kill(:INT, pid) rescue nil
|
197
|
+
Process.waitpid(pid) rescue nil
|
198
|
+
end
|
199
|
+
exit
|
200
|
+
end
|
201
|
+
sleep while 1
|
@@ -105,7 +105,10 @@ SQL
|
|
105
105
|
@prefetch_break_types = config[:prefetch_break_types] || []
|
106
106
|
|
107
107
|
@cleanup_interval = config[:cleanup_interval] || DEFAULT_DELETE_INTERVAL
|
108
|
-
|
108
|
+
# If cleanup_interval > max_request_per_child / max_acquire,
|
109
|
+
# some processes won't run DELETE query.
|
110
|
+
# (it's not an issue when there are enough workers)
|
111
|
+
@cleanup_interval_count = @cleanup_interval > 0 ? rand(@cleanup_interval) : 0
|
109
112
|
end
|
110
113
|
|
111
114
|
attr_reader :db
|
@@ -212,18 +215,22 @@ SQL
|
|
212
215
|
def acquire(alive_time, max_acquire, options)
|
213
216
|
now = (options[:now] || Time.now).to_i
|
214
217
|
next_timeout = now + alive_time
|
215
|
-
|
216
|
-
|
218
|
+
tasks = nil
|
219
|
+
t0 = nil
|
217
220
|
|
218
221
|
if @cleanup_interval_count <= 0
|
219
222
|
delete_timeout = now - DELETE_OFFSET
|
220
|
-
|
223
|
+
connect_locked {
|
224
|
+
t0=Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
225
|
+
@db["DELETE FROM `#{@table}` WHERE #{EVENT_HORIZON} < timeout && timeout <= ? AND created_at IS NULL", now].delete
|
221
226
|
@db["DELETE FROM `#{@table}` WHERE timeout <= ? AND created_at IS NULL", delete_timeout].delete
|
222
227
|
@cleanup_interval_count = @cleanup_interval
|
228
|
+
STDERR.puts"PQ:delete from #{@table}:%6f sec" % [Process.clock_gettime(Process::CLOCK_MONOTONIC)-t0]
|
223
229
|
}
|
224
230
|
end
|
225
231
|
|
226
232
|
connect_locked {
|
233
|
+
t0=Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
227
234
|
tasks = []
|
228
235
|
@db.fetch(@sql, now, now, max_acquire) {|row|
|
229
236
|
attributes = create_attributes(nil, row)
|
@@ -256,6 +263,8 @@ SQL
|
|
256
263
|
|
257
264
|
return tasks
|
258
265
|
}
|
266
|
+
ensure
|
267
|
+
STDERR.puts "PQ:acquire from #{@table}:%6f sec (%d tasks)" % [Process.clock_gettime(Process::CLOCK_MONOTONIC)-t0,tasks.size] if tasks
|
259
268
|
end
|
260
269
|
|
261
270
|
# => nil
|
data/lib/perfectqueue/version.rb
CHANGED
data/perfectqueue.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
gem.require_paths = ['lib']
|
19
19
|
|
20
|
+
gem.required_ruby_version = '>= 2.1'
|
20
21
|
gem.add_dependency "sequel", "~> 3.48.0"
|
21
22
|
gem.add_development_dependency "rake", "~> 0.9.2"
|
22
23
|
gem.add_development_dependency "rspec", "~> 3.3.0"
|
data/spec/supervisor_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: perfectqueue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.48
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -84,6 +84,7 @@ description: Highly available distributed cron built on RDBMS
|
|
84
84
|
email: frsyuki@gmail.com
|
85
85
|
executables:
|
86
86
|
- perfectqueue
|
87
|
+
- stress
|
87
88
|
extensions: []
|
88
89
|
extra_rdoc_files: []
|
89
90
|
files:
|
@@ -96,6 +97,7 @@ files:
|
|
96
97
|
- README.md
|
97
98
|
- Rakefile
|
98
99
|
- bin/perfectqueue
|
100
|
+
- bin/stress
|
99
101
|
- circle.yml
|
100
102
|
- lib/perfectqueue.rb
|
101
103
|
- lib/perfectqueue/application.rb
|
@@ -163,7 +165,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
163
165
|
requirements:
|
164
166
|
- - ">="
|
165
167
|
- !ruby/object:Gem::Version
|
166
|
-
version: '
|
168
|
+
version: '2.1'
|
167
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
168
170
|
requirements:
|
169
171
|
- - ">="
|
@@ -200,4 +202,3 @@ test_files:
|
|
200
202
|
- spec/task_metadata_spec.rb
|
201
203
|
- spec/task_monitor_spec.rb
|
202
204
|
- spec/task_spec.rb
|
203
|
-
has_rdoc: false
|