perfectqueue 0.8.47 → 0.8.48

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: 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