perfectqueue 0.8.49 → 0.8.54
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +19 -0
- data/.travis.yml +7 -3
- data/ChangeLog +21 -0
- data/lib/perfectqueue/backend/rdb.rb +19 -8
- data/lib/perfectqueue/backend/rdb_compat.rb +27 -8
- data/lib/perfectqueue/multiprocess/child_process_monitor.rb +2 -2
- data/lib/perfectqueue/multiprocess/thread_processor.rb +1 -1
- data/lib/perfectqueue/version.rb +1 -1
- data/lib/perfectqueue/worker.rb +4 -2
- data/spec/rdb_backend_spec.rb +15 -1
- data/spec/rdb_compat_backend_spec.rb +13 -1
- metadata +4 -5
- data/circle.yml +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed8791f086ecef1178ae6d001dc4addeef32a14f
|
4
|
+
data.tar.gz: 342eb4a9e95c7a5fdcc29c909f1f7a99c1e236ea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4ca28812f77cfad84f2f4198de80093b41b46ae26f8ebf0fc13fe8054c785e7e24f766baf64839af1a4c0fadc8f38073e5b1abd7c962ad97a32f859a2b5b237e
|
7
|
+
data.tar.gz: 13f80b70c03b3a43538f7f687869db8918eb5a319ce7fc2503963d21a0f4fd5fa3f3a1d0163f6e19424c0001061f2b049e80287261fd230a3628e5cae1fd4f76
|
@@ -0,0 +1,19 @@
|
|
1
|
+
version: 2.1
|
2
|
+
|
3
|
+
executors:
|
4
|
+
executor-circle:
|
5
|
+
docker:
|
6
|
+
- image: buildpack-deps:jessie
|
7
|
+
working_directory: /tmp
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
build:
|
11
|
+
executor: executor-circle
|
12
|
+
steps:
|
13
|
+
- run: echo "It has used Travis instead of CircleCI."
|
14
|
+
|
15
|
+
workflows:
|
16
|
+
version: 2
|
17
|
+
build_and_test:
|
18
|
+
jobs:
|
19
|
+
- build
|
data/.travis.yml
CHANGED
data/ChangeLog
CHANGED
@@ -1,3 +1,24 @@
|
|
1
|
+
== 2020-09-10 version 0.8.54
|
2
|
+
|
3
|
+
* Enhance logging on inter-process communication (#68)
|
4
|
+
|
5
|
+
== 2019-10-10 version 0.8.53
|
6
|
+
|
7
|
+
* Extract max_retry_count as config parameter (#66)
|
8
|
+
|
9
|
+
== 2019-07-31 version 0.8.52
|
10
|
+
|
11
|
+
* Add sleep lock retrying with exponential-backoff (#64)
|
12
|
+
|
13
|
+
== 2016-10-24 version 0.8.51
|
14
|
+
|
15
|
+
* Retry on temporary DatabaseConnectionError (#59)
|
16
|
+
|
17
|
+
== 2016-08-18 version 0.8.50
|
18
|
+
|
19
|
+
* Decrease GET_LOCK timeout from 60 to 10 seconds
|
20
|
+
* Add 0.5 to 30 seconds of sleep between GET_LOCK retrying
|
21
|
+
|
1
22
|
== 2016-08-02 version 0.8.49
|
2
23
|
|
3
24
|
* Revert v0.8.44 migration path (#38)
|
@@ -4,7 +4,6 @@ require_relative 'rdb_compat'
|
|
4
4
|
|
5
5
|
module PerfectQueue::Backend
|
6
6
|
class RDBBackend
|
7
|
-
MAX_RETRY = ::PerfectQueue::Backend::RDBCompatBackend::MAX_RETRY
|
8
7
|
DELETE_OFFSET = ::PerfectQueue::Backend::RDBCompatBackend::DELETE_OFFSET
|
9
8
|
class Token < Struct.new(:key)
|
10
9
|
end
|
@@ -21,6 +20,9 @@ module PerfectQueue::Backend
|
|
21
20
|
host: u.host,
|
22
21
|
port: u.port ? u.port.to_i : 3306
|
23
22
|
}
|
23
|
+
@pq_connect_timeout = config.fetch(:pq_connect_timeout, 20)
|
24
|
+
@max_retry_count = config.fetch(:max_retry_count, 10)
|
25
|
+
options[:connect_timeout] = config.fetch(:connect_timeout, 3)
|
24
26
|
options[:sslca] = config[:sslca] if config[:sslca]
|
25
27
|
db_name = u.path.split('/')[1]
|
26
28
|
@db = Sequel.mysql2(db_name, options)
|
@@ -32,6 +34,7 @@ module PerfectQueue::Backend
|
|
32
34
|
end
|
33
35
|
|
34
36
|
attr_reader :db
|
37
|
+
attr_reader :max_retry_count
|
35
38
|
|
36
39
|
def submit(id, data, time=Process.clock_gettime(Process::CLOCK_REALTIME, :second), resource=nil, max_running=nil)
|
37
40
|
connect {
|
@@ -54,23 +57,31 @@ module PerfectQueue::Backend
|
|
54
57
|
end
|
55
58
|
|
56
59
|
private
|
57
|
-
def connect
|
60
|
+
def connect
|
61
|
+
tmax = Process.clock_gettime(Process::CLOCK_REALTIME, :second) + @pq_connect_timeout
|
58
62
|
@mutex.synchronize do
|
59
63
|
retry_count = 0
|
60
64
|
begin
|
61
|
-
|
65
|
+
yield
|
66
|
+
rescue Sequel::DatabaseConnectionError
|
67
|
+
if (retry_count += 1) < @max_retry_count && tmax > Process.clock_gettime(Process::CLOCK_REALTIME, :second)
|
68
|
+
STDERR.puts "#{$!}\n retrying."
|
69
|
+
sleep 2
|
70
|
+
retry
|
71
|
+
end
|
72
|
+
STDERR.puts "#{$!}\n abort."
|
73
|
+
raise
|
62
74
|
rescue
|
63
75
|
# workaround for "Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction" error
|
64
76
|
if $!.to_s.include?('try restarting transaction')
|
65
|
-
err =
|
77
|
+
err = $!.backtrace.map{|bt| " #{bt}" }.unshift($!).join("\n")
|
66
78
|
retry_count += 1
|
67
|
-
if retry_count <
|
68
|
-
STDERR.puts err
|
79
|
+
if retry_count < @max_retry_count
|
80
|
+
STDERR.puts "#{err}\n retrying."
|
69
81
|
sleep 0.5
|
70
82
|
retry
|
71
|
-
else
|
72
|
-
STDERR.puts err + "\n abort."
|
73
83
|
end
|
84
|
+
STDERR.puts "#{err}\n abort."
|
74
85
|
end
|
75
86
|
raise
|
76
87
|
ensure
|
@@ -33,12 +33,17 @@ module PerfectQueue
|
|
33
33
|
DELETE_OFFSET = 10_0000_0000
|
34
34
|
EVENT_HORIZON = 13_0000_0000 # 2011-03-13 07:06:40 UTC
|
35
35
|
|
36
|
+
LOCK_RETRY_INITIAL_INTERVAL = 0.5
|
37
|
+
LOCK_RETRY_MAX_INTERVAL = 30
|
38
|
+
|
36
39
|
class Token < Struct.new(:key)
|
37
40
|
end
|
38
41
|
|
39
42
|
def initialize(client, config)
|
40
43
|
super
|
41
44
|
|
45
|
+
@pq_connect_timeout = config.fetch(:pq_connect_timeout, 20)
|
46
|
+
@max_retry_count = config.fetch(:max_retry_count, 10)
|
42
47
|
url = config[:url]
|
43
48
|
@table = config[:table]
|
44
49
|
unless @table
|
@@ -46,7 +51,9 @@ module PerfectQueue
|
|
46
51
|
end
|
47
52
|
|
48
53
|
if /\Amysql2:/i =~ url
|
49
|
-
|
54
|
+
options = {max_connections: 1, sslca: config[:sslca]}
|
55
|
+
options[:connect_timeout] = config.fetch(:connect_timeout, 3)
|
56
|
+
@db = Sequel.connect(url, options)
|
50
57
|
if config.fetch(:use_connection_pooling, nil) != nil
|
51
58
|
@use_connection_pooling = !!config[:use_connection_pooling]
|
52
59
|
else
|
@@ -54,11 +61,14 @@ module PerfectQueue
|
|
54
61
|
end
|
55
62
|
@table_lock = lambda {
|
56
63
|
locked = nil
|
64
|
+
interval = LOCK_RETRY_INITIAL_INTERVAL
|
57
65
|
loop do
|
58
66
|
@db.fetch("SELECT GET_LOCK('#{@table}', #{LOCK_WAIT_TIMEOUT}) locked") do |row|
|
59
67
|
locked = true if row[:locked] == 1
|
60
68
|
end
|
61
69
|
break if locked
|
70
|
+
sleep interval
|
71
|
+
interval = [interval * 2, LOCK_RETRY_MAX_INTERVAL].min
|
62
72
|
end
|
63
73
|
}
|
64
74
|
@table_unlock = lambda {
|
@@ -112,10 +122,10 @@ SQL
|
|
112
122
|
end
|
113
123
|
|
114
124
|
attr_reader :db
|
125
|
+
attr_reader :max_retry_count
|
115
126
|
|
116
127
|
KEEPALIVE = 10
|
117
|
-
|
118
|
-
LOCK_WAIT_TIMEOUT = 60
|
128
|
+
LOCK_WAIT_TIMEOUT = 10
|
119
129
|
DEFAULT_DELETE_INTERVAL = 20
|
120
130
|
|
121
131
|
def init_database(options)
|
@@ -333,7 +343,7 @@ SQL
|
|
333
343
|
end
|
334
344
|
|
335
345
|
protected
|
336
|
-
def connect_locked
|
346
|
+
def connect_locked
|
337
347
|
connect {
|
338
348
|
locked = false
|
339
349
|
|
@@ -343,7 +353,7 @@ SQL
|
|
343
353
|
locked = true
|
344
354
|
end
|
345
355
|
|
346
|
-
return
|
356
|
+
return yield
|
347
357
|
ensure
|
348
358
|
if @use_connection_pooling && locked
|
349
359
|
@table_unlock.call
|
@@ -352,22 +362,31 @@ SQL
|
|
352
362
|
}
|
353
363
|
end
|
354
364
|
|
355
|
-
def connect
|
365
|
+
def connect
|
356
366
|
now = Time.now.to_i
|
367
|
+
tmax = now + @pq_connect_timeout
|
357
368
|
@mutex.synchronize do
|
358
369
|
# keepalive_timeout
|
359
370
|
@db.disconnect if now - @last_time > KEEPALIVE
|
360
371
|
|
361
372
|
count = 0
|
362
373
|
begin
|
363
|
-
|
374
|
+
yield
|
364
375
|
@last_time = now
|
376
|
+
rescue Sequel::DatabaseConnectionError
|
377
|
+
if (count += 1) < @max_retry_count && tmax > Time.now.to_i
|
378
|
+
STDERR.puts "#{$!}\n retrying."
|
379
|
+
sleep 2
|
380
|
+
retry
|
381
|
+
end
|
382
|
+
STDERR.puts "#{$!}\n abort."
|
383
|
+
raise
|
365
384
|
rescue
|
366
385
|
# workaround for "Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction" error
|
367
386
|
if $!.to_s.include?('try restarting transaction')
|
368
387
|
err = ([$!] + $!.backtrace.map {|bt| " #{bt}" }).join("\n")
|
369
388
|
count += 1
|
370
|
-
if count <
|
389
|
+
if count < @max_retry_count
|
371
390
|
STDERR.puts err + "\n retrying."
|
372
391
|
sleep rand
|
373
392
|
retry
|
@@ -102,8 +102,8 @@ module PerfectQueue
|
|
102
102
|
def send_signal(sig)
|
103
103
|
begin
|
104
104
|
Process.kill(sig, @pid)
|
105
|
-
rescue Errno::ESRCH, Errno::EPERM
|
106
|
-
#
|
105
|
+
rescue Errno::ESRCH, Errno::EPERM => e
|
106
|
+
@log.info "#{e.class}: #{e.message}\n#{e.backtrace}"
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
@@ -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}
|
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)
|
data/lib/perfectqueue/version.rb
CHANGED
data/lib/perfectqueue/worker.rb
CHANGED
@@ -28,6 +28,7 @@ module PerfectQueue
|
|
28
28
|
config = block.call
|
29
29
|
|
30
30
|
@config = config
|
31
|
+
@log = config[:logger] || Logger.new(STDERR)
|
31
32
|
@runner = runner
|
32
33
|
|
33
34
|
@detach_wait = config[:detach_wait] || config['detach_wait'] || 10.0
|
@@ -66,9 +67,11 @@ module PerfectQueue
|
|
66
67
|
|
67
68
|
else
|
68
69
|
# child process finished unexpectedly
|
70
|
+
@log.info "Child process finished unexpectedly pid=#{pid}"
|
69
71
|
end
|
70
72
|
|
71
|
-
rescue Errno::ECHILD
|
73
|
+
rescue Errno::ECHILD => e
|
74
|
+
@log.info "#{e.class}: #{e.message}\n#{e.backtrace}"
|
72
75
|
end
|
73
76
|
end
|
74
77
|
|
@@ -130,4 +133,3 @@ module PerfectQueue
|
|
130
133
|
end
|
131
134
|
|
132
135
|
end
|
133
|
-
|
data/spec/rdb_backend_spec.rb
CHANGED
@@ -55,7 +55,7 @@ describe Backend::RDBBackend do
|
|
55
55
|
end
|
56
56
|
context 'error' do
|
57
57
|
it 'returns block result' do
|
58
|
-
expect(RuntimeError).to receive(:new).exactly(
|
58
|
+
expect(RuntimeError).to receive(:new).exactly(db.max_retry_count).and_call_original
|
59
59
|
allow(STDERR).to receive(:puts)
|
60
60
|
allow(db).to receive(:sleep)
|
61
61
|
expect do
|
@@ -65,5 +65,19 @@ describe Backend::RDBBackend do
|
|
65
65
|
end.to raise_error(RuntimeError)
|
66
66
|
end
|
67
67
|
end
|
68
|
+
context 'cannot connect' do
|
69
|
+
let (:uri){ 'mysql2://root:@nonexistent/perfectqueue_test' }
|
70
|
+
let (:db) do
|
71
|
+
Backend::RDBBackend.new(uri, table)
|
72
|
+
end
|
73
|
+
it 'raises Sequel::DatabaseConnectionError' do
|
74
|
+
allow(STDERR).to receive(:puts)
|
75
|
+
slept = 0
|
76
|
+
expect(db).to receive(:sleep).exactly(9).times{|n| slept += n }
|
77
|
+
expect(db.db).to receive(:connect).exactly(10).times.and_call_original
|
78
|
+
expect{ db.__send__(:connect){ db.db.run('SELECT 1;') } }.to raise_error(Sequel::DatabaseConnectionError)
|
79
|
+
expect(slept).to be < 30
|
80
|
+
end
|
81
|
+
end
|
68
82
|
end
|
69
83
|
end
|
@@ -367,7 +367,7 @@ describe Backend::RDBCompatBackend do
|
|
367
367
|
end
|
368
368
|
context 'error' do
|
369
369
|
it 'returns block result' do
|
370
|
-
expect(RuntimeError).to receive(:new).exactly(
|
370
|
+
expect(RuntimeError).to receive(:new).exactly(db.max_retry_count).and_call_original
|
371
371
|
allow(STDERR).to receive(:puts)
|
372
372
|
allow(db).to receive(:sleep)
|
373
373
|
expect do
|
@@ -376,6 +376,18 @@ describe Backend::RDBCompatBackend do
|
|
376
376
|
end
|
377
377
|
end.to raise_error(RuntimeError)
|
378
378
|
end
|
379
|
+
context 'cannot connect' do
|
380
|
+
let (:config){ {url: 'mysql2://root:@nonexistent/perfectqueue_test', table: table} }
|
381
|
+
it 'raises Sequel::DatabaseConnectionError' do
|
382
|
+
allow(STDERR).to receive(:puts)
|
383
|
+
d = Backend::RDBCompatBackend.new(client, config)
|
384
|
+
slept = 0
|
385
|
+
expect(d).to receive(:sleep).exactly(9).times{|n| slept += n }
|
386
|
+
expect(d.db).to receive(:connect).exactly(10).times.and_call_original
|
387
|
+
expect{ d.__send__(:connect){ d.db.run('SELECT 1;') } }.to raise_error(Sequel::DatabaseConnectionError)
|
388
|
+
expect(slept).to eq(18)
|
389
|
+
end
|
390
|
+
end
|
379
391
|
end
|
380
392
|
end
|
381
393
|
|
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.54
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sadayuki Furuhashi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -88,6 +88,7 @@ executables:
|
|
88
88
|
extensions: []
|
89
89
|
extra_rdoc_files: []
|
90
90
|
files:
|
91
|
+
- ".circleci/config.yml"
|
91
92
|
- ".gitignore"
|
92
93
|
- ".travis.yml"
|
93
94
|
- ChangeLog
|
@@ -98,7 +99,6 @@ files:
|
|
98
99
|
- Rakefile
|
99
100
|
- bin/perfectqueue
|
100
101
|
- bin/stress
|
101
|
-
- circle.yml
|
102
102
|
- lib/perfectqueue.rb
|
103
103
|
- lib/perfectqueue/application.rb
|
104
104
|
- lib/perfectqueue/application/base.rb
|
@@ -177,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
177
|
version: '0'
|
178
178
|
requirements: []
|
179
179
|
rubyforge_project:
|
180
|
-
rubygems_version: 2.5.
|
180
|
+
rubygems_version: 2.5.2.3
|
181
181
|
signing_key:
|
182
182
|
specification_version: 4
|
183
183
|
summary: Highly available distributed cron built on RDBMS
|
@@ -208,4 +208,3 @@ test_files:
|
|
208
208
|
- spec/task_metadata_spec.rb
|
209
209
|
- spec/task_monitor_spec.rb
|
210
210
|
- spec/task_spec.rb
|
211
|
-
has_rdoc: false
|