perfectqueue 0.8.44.1 → 0.8.45

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: ff48a49e577eab839217d96f1efbe4c3bbb74174
4
- data.tar.gz: d7ddae1782d03ffccacfab5143d7dc08a2f33c1c
3
+ metadata.gz: ca67d530351a8a7e0ff2f950d10248bc23eb5e2e
4
+ data.tar.gz: 5b66bc3219182f721af358e0bce7836ffdca356d
5
5
  SHA512:
6
- metadata.gz: 59b93afc56e199edaee68619425bbba22bc63674f0f4ae4fe2b7dd6c1bde59e2c08cee771a0e60f1020c3d10a139b39e8fa7328c2714bc978d6220dd516d97de
7
- data.tar.gz: 58ba8303acadf21474055d62c9cab9dc4fdc734987f709e7063ba448e7240309eb7b83e63bae0c2ec10767a18561acb14e093443a028788f1eafe30e5ee16063
6
+ metadata.gz: 282b4e05105a5b6fe92cfb3c406b415221784350cb1449e87fe42b481fa7adc0b5d5318c67cc5922e9deafbd244939b2481e76c10ced8b15e981605eacddde35
7
+ data.tar.gz: 7f4a15e96b072fa04144d04f6f92396a730b76de07c0cb45c6f63d9c30d84bbfff88d7cab526a7a895d6265ed74cc897f9d0b8293d3a7c8271d67cba65c4acee
data/ChangeLog CHANGED
@@ -1,6 +1,9 @@
1
- == 2016-06-14 version 0.8.44.1
1
+ == 2015-12-02 version 0.8.45
2
2
 
3
- * Don't delete v0.8.46's finished tasks for migration
3
+ * remove sqlite3 support
4
+ * fix #3
5
+ * add specs
6
+ * fix other trivial bugs
4
7
 
5
8
  == 2015-10-07 version 0.8.44
6
9
 
@@ -19,7 +19,7 @@
19
19
  module PerfectQueue
20
20
  module Application
21
21
 
22
- class UndefinedDecisionError
22
+ class UndefinedDecisionError < StandardError
23
23
  end
24
24
 
25
25
  class Decider
@@ -39,7 +39,7 @@ module PerfectQueue
39
39
  begin
40
40
  m = method(type)
41
41
  rescue NameError
42
- raise UndefinedDecisionError, "Undefined decision #{type} options=#{opt.inspect}"
42
+ raise UndefinedDecisionError, "Undefined decision #{type} options=#{opts.inspect}"
43
43
  end
44
44
  m.call(opts)
45
45
  end
@@ -58,7 +58,7 @@ module PerfectQueue
58
58
  when String, Symbol
59
59
  pattern = /\A#{Regexp.escape(pattern)}\z/
60
60
  else
61
- raise ArguementError, "pattern should be String or Regexp but got #{pattern.class}: #{pattern.inspect}"
61
+ raise ArgumentError, "pattern should be String or Regexp but got #{pattern.class}: #{pattern.inspect}"
62
62
  end
63
63
 
64
64
  @patterns << [pattern, sym]
@@ -21,9 +21,6 @@ module PerfectQueue
21
21
  class RDBCompatBackend
22
22
  include BackendHelper
23
23
 
24
- # backport from v0.8.46
25
- EVENT_HORIZON = 13_0000_0000 # 2011-03-13 07:06:40 UTC
26
-
27
24
  class Token < Struct.new(:key)
28
25
  end
29
26
 
@@ -40,10 +37,7 @@ module PerfectQueue
40
37
  #password = config[:password]
41
38
  #user = config[:user]
42
39
 
43
- case url.split('//',2)[0].to_s
44
- when /sqlite/i
45
- @db = Sequel.connect(url, :max_connections=>1)
46
- when /mysql/i
40
+ if /\Amysql/i =~ url
47
41
  require 'uri'
48
42
 
49
43
  uri = URI.parse(url)
@@ -63,8 +57,20 @@ module PerfectQueue
63
57
  else
64
58
  @use_connection_pooling = !!config[:sslca]
65
59
  end
60
+ @table_lock = lambda {
61
+ locked = nil
62
+ loop do
63
+ @db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
64
+ locked = true if row[:locked] == 1
65
+ end
66
+ break if locked
67
+ end
68
+ }
69
+ @table_unlock = lambda {
70
+ @db.run("SELECT RELEASE_LOCK('#{@table}')")
71
+ }
66
72
  else
67
- raise ConfigError, "'sqlite' and 'mysql' are supported"
73
+ raise ConfigError, "only 'mysql' is supported"
68
74
  end
69
75
 
70
76
  @last_time = Time.now.to_i
@@ -79,7 +85,6 @@ module PerfectQueue
79
85
  SELECT id, timeout, data, created_at, resource
80
86
  FROM `#{@table}`
81
87
  WHERE timeout <= ? AND timeout <= ? AND created_at IS NOT NULL
82
- AND #{EVENT_HORIZON} < timeout
83
88
  ORDER BY timeout ASC
84
89
  LIMIT ?
85
90
  SQL
@@ -94,37 +99,11 @@ LEFT JOIN (
94
99
  GROUP BY resource
95
100
  ) AS R ON resource = res
96
101
  WHERE timeout <= ? AND created_at IS NOT NULL AND (max_running-running IS NULL OR max_running-running > 0)
97
- AND #{EVENT_HORIZON} < timeout
98
102
  ORDER BY weight DESC, timeout ASC
99
103
  LIMIT ?
100
104
  SQL
101
105
  end
102
106
 
103
- case url.split('//',2)[0].to_s
104
- when /sqlite/i
105
- # sqlite always locks tables on BEGIN
106
- @table_lock = nil
107
- @table_unlock = nil
108
- when /mysql/i
109
- @table_lock = lambda {
110
- locked = nil
111
- loop do
112
- @db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
113
- locked = true if row[:locked] == 1
114
- end
115
- break if locked
116
- end
117
- }
118
- @table_unlock = lambda {
119
- @db.run("SELECT RELEASE_LOCK('#{@table}')")
120
- }
121
- else
122
- @table_lock = lambda {
123
- @db.run("LOCK TABLE `#{@table}`")
124
- }
125
- @table_unlock = nil
126
- end
127
-
128
107
  @prefetch_break_types = config[:prefetch_break_types] || []
129
108
 
130
109
  @cleanup_interval = config[:cleanup_interval] || DEFAULT_DELETE_INTERVAL
@@ -190,6 +169,24 @@ SQL
190
169
  }
191
170
  end
192
171
 
172
+ def compress_data(data, compression)
173
+ if compression == 'gzip'
174
+ require 'zlib'
175
+ require 'stringio'
176
+ io = StringIO.new
177
+ io.set_encoding(Encoding::ASCII_8BIT)
178
+ gz = Zlib::GzipWriter.new(io)
179
+ begin
180
+ gz.write(data)
181
+ ensure
182
+ gz.close
183
+ end
184
+ data = io.string
185
+ data = Sequel::SQL::Blob.new(data)
186
+ end
187
+ data
188
+ end
189
+
193
190
  # => Task
194
191
  def submit(key, type, data, options)
195
192
  now = (options[:now] || Time.now).to_i
@@ -200,23 +197,7 @@ SQL
200
197
  max_running = options[:max_running]
201
198
  data = data ? data.dup : {}
202
199
  data['type'] = type
203
-
204
- d = data.to_json
205
-
206
- if options[:compression] == 'gzip'
207
- require 'zlib'
208
- require 'stringio'
209
- io = StringIO.new
210
- gz = Zlib::GzipWriter.new(io)
211
- begin
212
- gz.write(d)
213
- ensure
214
- gz.close
215
- end
216
- d = io.string
217
- d.force_encoding('ASCII-8BIT') if d.respond_to?(:force_encoding)
218
- d = Sequel::SQL::Blob.new(d)
219
- end
200
+ d = compress_data(data.to_json, options[:compression])
220
201
 
221
202
  connect {
222
203
  begin
@@ -240,7 +221,7 @@ SQL
240
221
 
241
222
  if @cleanup_interval_count <= 0
242
223
  connect_locked {
243
- @db["DELETE FROM `#{@table}` WHERE #{EVENT_HORIZON} < timeout && timeout <= ? AND created_at IS NULL", now].delete
224
+ @db["DELETE FROM `#{@table}` WHERE timeout <= ? AND created_at IS NULL", now].delete
244
225
  @cleanup_interval_count = @cleanup_interval
245
226
  }
246
227
  end
@@ -281,8 +262,6 @@ SQL
281
262
 
282
263
  # => nil
283
264
  def cancel_request(key, options)
284
- now = (options[:now] || Time.now).to_i
285
-
286
265
  # created_at=0 means cancel_requested
287
266
  connect {
288
267
  n = @db["UPDATE `#{@table}` SET created_at=0 WHERE id=? AND created_at IS NOT NULL", key].update
@@ -323,7 +302,7 @@ SQL
323
302
  params = [sql, next_timeout]
324
303
  if data
325
304
  sql << ", data=?"
326
- params << data.to_json
305
+ params << compress_data(data.to_json, options[:compression])
327
306
  end
328
307
  sql << " WHERE id=? AND created_at IS NOT NULL"
329
308
  params << key
@@ -336,8 +315,6 @@ SQL
336
315
  raise PreemptedError, "task key=#{key} does not exist or preempted."
337
316
  elsif row[:created_at] == nil
338
317
  raise PreemptedError, "task key=#{key} preempted."
339
- elsif row[:created_at] <= 0
340
- raise CancelRequestedError, "task key=#{key} is cancel requested."
341
318
  else # row[:timeout] == next_timeout
342
319
  # ok
343
320
  end
@@ -395,6 +372,8 @@ SQL
395
372
  else
396
373
  STDERR.puts err + "\n abort."
397
374
  end
375
+ else
376
+ err = $!
398
377
  end
399
378
 
400
379
  STDERR.puts "disconnects current connection: #{err}"
@@ -411,6 +390,7 @@ SQL
411
390
  GZIP_MAGIC_BYTES = [0x1f, 0x8b].pack('CC')
412
391
 
413
392
  def create_attributes(now, row)
393
+ compression = nil
414
394
  if row[:created_at] === nil
415
395
  created_at = nil # unknown creation time
416
396
  status = TaskStatus::FINISHED
@@ -434,6 +414,7 @@ SQL
434
414
  if d[0, 2] == GZIP_MAGIC_BYTES
435
415
  require 'zlib'
436
416
  require 'stringio'
417
+ compression = 'gzip'
437
418
  gz = Zlib::GzipReader.new(StringIO.new(d))
438
419
  begin
439
420
  d = gz.read
@@ -464,6 +445,7 @@ SQL
464
445
  :max_running => row[:max_running],
465
446
  :message => nil, # not supported
466
447
  :node => nil, # not supported
448
+ :compression => compression,
467
449
  }
468
450
  end
469
451
 
@@ -56,7 +56,7 @@ module PerfectQueue
56
56
  @processors << @processor_class.new(@runner, @processors.size+1, config)
57
57
  end
58
58
  elsif extra < 0
59
- -extra.times do
59
+ (-extra).times do
60
60
  c = @processors.shift
61
61
  c.stop(immediate)
62
62
  c.join
@@ -106,10 +106,7 @@ module PerfectQueue
106
106
  def replace(immediate, command=[$0]+ARGV)
107
107
  return if @replaced_pid
108
108
  stop(immediate)
109
- @replaced_pid = Process.fork do
110
- exec(*command)
111
- exit!(127)
112
- end
109
+ @replaced_pid = Process.spawn(*command)
113
110
  self
114
111
  end
115
112
 
@@ -121,7 +121,7 @@ module PerfectQueue
121
121
  end
122
122
 
123
123
  def process(task)
124
- @log.info "acquired task task=#{task.key} id=#{@processor_id}: #{task.inspect}"
124
+ @log.info "acquired task task=#{task.key} id=#{@processor_id}"
125
125
  begin
126
126
  r = @runner.new(task)
127
127
  @tm.set_task(task, r)
@@ -58,6 +58,7 @@ module PerfectQueue
58
58
  class TaskWithMetadata < Task
59
59
  def initialize(client, key, attributes)
60
60
  super(client, key)
61
+ @compression = attributes.delete(:compression)
61
62
  @attributes = attributes
62
63
  end
63
64
 
@@ -93,7 +94,7 @@ module PerfectQueue
93
94
  def update_data!(hash)
94
95
  data = @attributes[:data] || {}
95
96
  merged = data.merge(hash)
96
- heartbeat!(:data => merged)
97
+ heartbeat!(data: merged, compression: compression)
97
98
  @attributes[:data] = merged
98
99
  end
99
100
 
@@ -19,6 +19,7 @@
19
19
  module PerfectQueue
20
20
  module TaskMetadataAccessors
21
21
  attr_reader :attributes
22
+ attr_reader :compression
22
23
 
23
24
  def type
24
25
  @attributes[:type]
@@ -60,10 +61,6 @@ module PerfectQueue
60
61
  status == TaskStatus::FINISHED
61
62
  end
62
63
 
63
- def running?
64
- status == TaskStatus::RUNNING
65
- end
66
-
67
64
  def waiting?
68
65
  status == TaskStatus::WAITING
69
66
  end
@@ -83,6 +80,7 @@ module PerfectQueue
83
80
  def initialize(client, key, attributes)
84
81
  super(client)
85
82
  @key = key
83
+ @compression = attributes.delete(:compression)
86
84
  @attributes = attributes
87
85
  end
88
86
 
@@ -1,3 +1,3 @@
1
1
  module PerfectQueue
2
- VERSION = "0.8.44.1"
2
+ VERSION = "0.8.45"
3
3
  end
@@ -19,8 +19,7 @@ Gem::Specification.new do |gem|
19
19
 
20
20
  gem.add_dependency "sequel", "~> 3.48.0"
21
21
  gem.add_development_dependency "rake", "~> 0.9.2"
22
- gem.add_development_dependency "rspec", "~> 2.10.0"
22
+ gem.add_development_dependency "rspec", "~> 3.3.0"
23
23
  gem.add_development_dependency "simplecov", "~> 0.10.0"
24
- gem.add_development_dependency "sqlite3", "~> 1.3.3"
25
24
  gem.add_development_dependency "mysql2", "~> 0.3.20"
26
25
  end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe PerfectQueue::Application::Base do
4
+ describe '.decider=' do
5
+ it 'defines .decider which returns the decider' do
6
+ decider_klass = double('decider_klass')
7
+ klass = PerfectQueue::Application::Base
8
+ allow(klass).to receive(:decider).and_call_original
9
+ allow(klass).to receive(:decider=).with(decider_klass).and_call_original
10
+ expect(klass.decider = decider_klass).to eq(decider_klass)
11
+ expect(klass.decider).to eq(decider_klass)
12
+ end
13
+ end
14
+
15
+ describe '.decider' do
16
+ it 'returns DefaultDecider' do
17
+ expect(PerfectQueue::Application::Base.decider).to eq(PerfectQueue::Application::DefaultDecider)
18
+ end
19
+ end
20
+
21
+ describe '#new' do
22
+ let (:task){ double('task') }
23
+ let (:base) { PerfectQueue::Application::Base.new(task) }
24
+ it 'calls super and set decider'do
25
+ expect(base).to be_an_instance_of(PerfectQueue::Application::Base)
26
+ expect(base.instance_variable_get(:@task)).to eq(task)
27
+ expect(base.instance_variable_get(:@decider)).to be_an_instance_of(Application::DefaultDecider)
28
+ end
29
+ end
30
+
31
+ describe '#run' do
32
+ let (:base) { PerfectQueue::Application::Base.new(double('task')) }
33
+ it 'returns nil if before_perform returns false' do
34
+ allow(base).to receive(:before_perform).and_return(false)
35
+ expect(base.run).to be_nil
36
+ end
37
+ it 'returns nil' do
38
+ expect(base).to receive(:before_perform).exactly(:once).and_call_original
39
+ expect(base).to receive(:perform).exactly(:once).and_return(nil)
40
+ expect(base).to receive(:after_perform).exactly(:once).and_call_original
41
+ expect(base.run).to be_nil
42
+ end
43
+ it 'calls unexpected_error_raised on error' do
44
+ allow(base).to receive(:before_perform).exactly(:once).and_call_original
45
+ allow(base).to receive(:perform).exactly(:once) { raise }
46
+ allow(base).to receive(:decide!).with(:unexpected_error_raised, error: kind_of(Exception)).exactly(:once)
47
+ expect(base.run).to be_nil
48
+ end
49
+ end
50
+
51
+ describe '#before_perform' do
52
+ let (:base) { PerfectQueue::Application::Base.new(double('task')) }
53
+ it 'returns true' do
54
+ expect(base.before_perform).to be true
55
+ end
56
+ end
57
+
58
+ describe '#after_perform' do
59
+ let (:base) { PerfectQueue::Application::Base.new(double('task')) }
60
+ it 'returns nil' do
61
+ expect(base.after_perform).to be_nil
62
+ end
63
+ end
64
+
65
+ describe '#decide!' do
66
+ let (:base) do
67
+ decider = double('decider')
68
+ expect(decider).to receive(:decide!).with(:type, :option).exactly(:once)
69
+ decider_klass = double('decider_klass')
70
+ allow(decider_klass).to receive(:new).with(kind_of(PerfectQueue::Application::Base)).and_return(decider)
71
+ klass = PerfectQueue::Application::Base
72
+ allow(klass).to receive(:decider).and_call_original
73
+ allow(klass).to receive(:decider=).with(decider_klass).and_call_original
74
+ klass.decider = decider_klass
75
+ klass.new(double('task'))
76
+ end
77
+ it 'calls decider.decide' do
78
+ expect(base.decide!(:type, :option)).to be_nil
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe PerfectQueue::Application::UndefinedDecisionError do
4
+ it { is_expected.to be_an_instance_of(PerfectQueue::Application::UndefinedDecisionError) }
5
+ it { is_expected.to be_a(Exception) }
6
+ end
7
+
8
+ describe PerfectQueue::Application::Decider do
9
+ describe '#new' do
10
+ let (:decider) { PerfectQueue::Application::Decider.new(nil) }
11
+ it do
12
+ expect(decider).to be_an_instance_of(PerfectQueue::Application::Decider)
13
+ end
14
+ end
15
+
16
+ describe '#queue' do
17
+ let (:queue){ double('queue') }
18
+ let (:decider) do
19
+ base = double('base')
20
+ allow(base).to receive(:queue).exactly(:once).and_return(queue)
21
+ PerfectQueue::Application::Decider.new(base)
22
+ end
23
+ it 'calls @base.queue' do
24
+ expect(decider.queue).to eq(queue)
25
+ end
26
+ end
27
+
28
+ describe '#task' do
29
+ let (:task){ double('task') }
30
+ let (:decider) do
31
+ base = double('base')
32
+ allow(base).to receive(:task).exactly(:once).and_return(task)
33
+ PerfectQueue::Application::Decider.new(base)
34
+ end
35
+ it 'calls @base.task' do
36
+ expect(decider.task).to eq(task)
37
+ end
38
+ end
39
+
40
+ describe '#decide!' do
41
+ let (:decider) { PerfectQueue::Application::Decider.new(nil) }
42
+ it 'calls the specified method' do
43
+ allow(decider).to receive(:foo).exactly(:once).with(72).and_return(42)
44
+ expect(decider.decide!(:foo, 72)).to eq(42)
45
+ end
46
+ it 'raises UndefinedDecisionError on unknown method' do
47
+ expect{ decider.decide!(:foo, 72) }.to raise_error(PerfectQueue::Application::UndefinedDecisionError)
48
+ end
49
+ end
50
+ end
51
+
52
+ describe PerfectQueue::Application::DefaultDecider do
53
+ subject { PerfectQueue::Application::DefaultDecider.new(nil) }
54
+ it { is_expected.to be_a(PerfectQueue::Application::Decider) }
55
+ it { is_expected.to be_an_instance_of(PerfectQueue::Application::DefaultDecider) }
56
+ end