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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 68a42f45b43150785b9c0a0ad1e6757da6b8104f
4
- data.tar.gz: 64b1df2f881992eaf48f1811b323966c35cdfccd
3
+ metadata.gz: 56f251e9261948702f9f058179343788c08ea08e
4
+ data.tar.gz: 4bd8336b27bfc2097a22581120e872fe621d4c53
5
5
  SHA512:
6
- metadata.gz: aa3effbb36e362da777ef15ab8aba0feb76471d150e7ae887cf299d90cf48d785f90b3f976907ad0e0fe6256ea7dd4bfa5198676e931a581778aae5c3dcfb677
7
- data.tar.gz: 4235badb3ba5fdc97d570e14fed8c3d405c7b9d5fe98ee7962b7df3bc2bfef730a275aef241418d61fdc1d9a02e0b8401a8e5979a53af21c59428bd2be5ab5d3
6
+ metadata.gz: 0a826da88811b6f511b4e4b0a6bdab9255a0a792848874b761fb9c2b6014fb3ac32f1c2a8231c8bdd54dfec82bb37fbd46b87664d392f829fb9fadfcb4388d3e
7
+ data.tar.gz: 4591e5a7f253502dafb1687ce57d572fa8b7742b2482be6938d41ce501d9e42f44cc4a2ca3e9a13a4a7de6f50f3518831b2bc9ba60bbac5ec29930ea67c36602
data/.travis.yml CHANGED
@@ -1,8 +1,7 @@
1
1
  rvm:
2
- - 1.9.3
3
- - 2.0.0
4
- - 2.1.6
5
- - 2.2.2
2
+ - 2.1.10
3
+ - 2.2.5
4
+ - 2.3.1
6
5
  - ruby-head
7
6
 
8
7
  script: "bundle exec rake spec"
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 cancel_request k2
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
- @cleanup_interval_count = 0
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
- tasks = []
218
+ tasks = nil
219
+ t0 = nil
217
220
 
218
221
  if @cleanup_interval_count <= 0
219
222
  delete_timeout = now - DELETE_OFFSET
220
- connect { # TODO: HERE should be still connect_locked ?
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
@@ -1,3 +1,3 @@
1
1
  module PerfectQueue
2
- VERSION = "0.8.47"
2
+ VERSION = "0.8.48"
3
3
  end
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"
@@ -182,7 +182,7 @@ describe Supervisor do
182
182
  queue.submit('task01', 'test', {'sleep'=>4})
183
183
  sleep 2
184
184
  Process.kill(:TERM, Process.pid)
185
- expect(@thread.join(3)).to eq(@thread)
185
+ expect(@thread.join(5)).to eq(@thread)
186
186
  end
187
187
  end
188
188
 
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.47
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-04-15 00:00:00.000000000 Z
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: '0'
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