resque_stuck_queue_revised 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +19 -0
- data/LICENSE.txt +22 -0
- data/README.md +199 -0
- data/Rakefile +26 -0
- data/THOUGHTS +9 -0
- data/lib/resque/stuck_queue.rb +1 -0
- data/lib/resque_stuck_queue.rb +320 -0
- data/lib/resque_stuck_queue/config.rb +81 -0
- data/lib/resque_stuck_queue/heartbeat_job.rb +19 -0
- data/lib/resque_stuck_queue/version.rb +5 -0
- data/resque_stuck_queue.gemspec +27 -0
- data/test/resque/set_redis_key.rb +9 -0
- data/test/test_collision.rb +47 -0
- data/test/test_config.rb +67 -0
- data/test/test_helper.rb +57 -0
- data/test/test_integration.rb +172 -0
- data/test/test_lagtime.rb +34 -0
- data/test/test_named_queues.rb +96 -0
- data/test/test_resque_stuck_queue.rb +58 -0
- data/test/test_set_custom_refresh_job.rb +41 -0
- data/test/test_ver_2.rb +45 -0
- metadata +132 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'minitest'
|
2
|
+
require "minitest/autorun"
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
$:.unshift(".")
|
6
|
+
require 'resque_stuck_queue'
|
7
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "resque", "set_redis_key")
|
8
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
|
9
|
+
|
10
|
+
class TestLagTime < Minitest::Test
|
11
|
+
|
12
|
+
include TestHelper
|
13
|
+
|
14
|
+
def setup
|
15
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
16
|
+
Resque::StuckQueue.redis.flushall
|
17
|
+
Resque::StuckQueue.config[:abort_on_exception] = true
|
18
|
+
Resque::StuckQueue.config[:watcher_interval] = 1
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_triggers_handler_with_lagtime
|
22
|
+
Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
|
23
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
24
|
+
@lagtime = 0
|
25
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { |queue_name, lagtime| @lagtime = lagtime }
|
26
|
+
start_and_stop_loops_after(5)
|
27
|
+
|
28
|
+
# check handler did get called
|
29
|
+
assert @lagtime > 0, "lagtime shoudl be set"
|
30
|
+
assert @lagtime < 5, "lagtime shoudl be set"
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
|
2
|
+
|
3
|
+
class TestNamedQueues < Minitest::Test
|
4
|
+
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Resque::StuckQueue.config[:trigger_timeout] = 1
|
9
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
10
|
+
Resque::StuckQueue.config[:abort_on_exception] = true
|
11
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
12
|
+
Resque::StuckQueue.config[:watcher_interval] = 1
|
13
|
+
Resque::StuckQueue.redis.flushall
|
14
|
+
end
|
15
|
+
|
16
|
+
def teardown
|
17
|
+
hax_kill_resque
|
18
|
+
Resque::StuckQueue.force_stop!
|
19
|
+
Process.waitpid(@resque_pid) if @resque_pid
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_no_custom_queues_defaults_to_app
|
23
|
+
puts "#{__method__}"
|
24
|
+
Resque::StuckQueue.config[:queues] = nil
|
25
|
+
start_and_stop_loops_after(2)
|
26
|
+
assert Resque::StuckQueue.heartbeat_keys.include?("app:resque-stuck-queue"), 'has global keys'
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_has_custom_queues
|
30
|
+
puts "#{__method__}"
|
31
|
+
Resque::StuckQueue.config[:queues] = [:foo,:bar]
|
32
|
+
assert Resque::StuckQueue.heartbeat_keys.include?("foo:resque-stuck-queue"), 'has global keys'
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_resque_enqueues_a_job_with_resqueue_running_but_on_that_queue_does_trigger
|
36
|
+
puts "#{__method__}"
|
37
|
+
Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
|
38
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
39
|
+
Resque::StuckQueue.config[:queues] = [:custom_queue_name]
|
40
|
+
@triggered = false
|
41
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { |queue_name| @triggered = queue_name }
|
42
|
+
Resque::StuckQueue.start_in_background
|
43
|
+
|
44
|
+
# job gets enqueued successfully
|
45
|
+
@resque_pid = run_resque("no-such-jobs-for-this-queue")
|
46
|
+
sleep 2 # allow timeout to trigger
|
47
|
+
|
48
|
+
# check handler did get called
|
49
|
+
assert_equal @triggered, :custom_queue_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_resque_enqueues_a_job_correct_queue_does_not_trigger
|
53
|
+
puts "#{__method__}"
|
54
|
+
Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
|
55
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
56
|
+
Resque::StuckQueue.config[:queues] = [:custom_queue_name, :diff_one]
|
57
|
+
assert Resque::StuckQueue.heartbeat_keys.include?("custom_queue_name:resque-stuck-queue"), 'has global keys'
|
58
|
+
@triggered = false
|
59
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { @triggered = true }
|
60
|
+
@resque_pid = run_resque("custom_queue_name")
|
61
|
+
Resque::StuckQueue.start_in_background
|
62
|
+
sleep 2 # allow timeout to trigger
|
63
|
+
|
64
|
+
# check handler did not get called
|
65
|
+
assert_equal @triggered, false
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_triggers_once_and_then_recovers
|
69
|
+
# FIXME test refactoring wrong place for this test.
|
70
|
+
puts "#{__method__}"
|
71
|
+
|
72
|
+
Resque::StuckQueue.config[:trigger_timeout] = 2
|
73
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
74
|
+
Resque::StuckQueue.config[:queues] = [:app]
|
75
|
+
|
76
|
+
@triggered = 0
|
77
|
+
@recovered = 0
|
78
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { @triggered += 1 }
|
79
|
+
Resque::StuckQueue.config[:recovered_handler] = proc { @recovered += 1 }
|
80
|
+
|
81
|
+
Thread.new {
|
82
|
+
# mock a job going through after we trigger :recovered so we'll w/o doing a run_resque
|
83
|
+
Thread.current.abort_on_exception = true
|
84
|
+
sleep 3
|
85
|
+
Resque::StuckQueue.redis.set(Resque::StuckQueue.heartbeat_key_for(:app), Time.now.to_i)
|
86
|
+
}
|
87
|
+
start_and_stop_loops_after(4)
|
88
|
+
|
89
|
+
# check handler did get called ONCE
|
90
|
+
assert_equal @recovered, 1
|
91
|
+
assert_equal @triggered, 1
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
|
2
|
+
|
3
|
+
class TestResqueStuckQueue < Minitest::Test
|
4
|
+
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
def teardown
|
8
|
+
puts "#{__method__}"
|
9
|
+
Resque::StuckQueue.unstub(:read_from_redis)
|
10
|
+
end
|
11
|
+
|
12
|
+
def setup
|
13
|
+
puts "#{__method__}"
|
14
|
+
# clean previous test runs
|
15
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
16
|
+
Resque::StuckQueue.config[:watcher_interval] = 1
|
17
|
+
Resque::StuckQueue.redis.flushall
|
18
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1 # seconds
|
19
|
+
Resque::StuckQueue.config[:abort_on_exception] = true
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_configure_heartbeat_key
|
23
|
+
puts "#{__method__}"
|
24
|
+
assert_nil Resque::StuckQueue.redis.get("it-is-configurable"), "global key should not be set"
|
25
|
+
Resque::StuckQueue.config[:heartbeat_key] = "it-is-configurable"
|
26
|
+
start_and_stop_loops_after(3)
|
27
|
+
refute_nil Resque::StuckQueue.redis.get("app:it-is-configurable"), "global key should be set"
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_it_does_not_trigger_handler_if_under_max_time
|
31
|
+
puts "#{__method__}"
|
32
|
+
Resque::StuckQueue.config[:trigger_timeout] = 5
|
33
|
+
Resque::StuckQueue.stubs(:read_from_redis).returns(Time.now.to_i)
|
34
|
+
|
35
|
+
@triggered = false
|
36
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { @triggered = true }
|
37
|
+
start_and_stop_loops_after(3)
|
38
|
+
assert_equal false, @triggered # "handler should not be called"
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_stops_if_handler_raises
|
42
|
+
puts "#{__method__}"
|
43
|
+
Resque::StuckQueue.config[:trigger_timeout] = 1 # wait a short time, will trigger
|
44
|
+
Resque::StuckQueue.config[:abort_on_exception] = true # bubble up the raise
|
45
|
+
last_time_too_old = Time.now.to_i - Resque::StuckQueue::TRIGGER_TIMEOUT
|
46
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { raise "handler had bad sad!" }
|
47
|
+
begin
|
48
|
+
start_and_stop_loops_after(4)
|
49
|
+
sleep 4
|
50
|
+
assert false, "should raise"
|
51
|
+
rescue => e
|
52
|
+
puts e.inspect
|
53
|
+
assert true, "should raise handler bad sad #{e.inspect}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
|
2
|
+
|
3
|
+
class TestYourOwnRefreshJob < Minitest::Test
|
4
|
+
|
5
|
+
include TestHelper
|
6
|
+
|
7
|
+
def setup
|
8
|
+
Resque::StuckQueue.reset!
|
9
|
+
Resque::StuckQueue.config[:trigger_timeout] = 1
|
10
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
11
|
+
Resque::StuckQueue.config[:watcher_interval] = 1
|
12
|
+
Resque::StuckQueue.config[:abort_on_exception] = true
|
13
|
+
Resque::StuckQueue.config[:heartbeat_job] = nil
|
14
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
15
|
+
Resque::StuckQueue.redis.flushall
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_will_trigger_with_unrefreshing_custom_heartbeat_job
|
19
|
+
# it will trigger because the key will be unrefreshed, hence 'old' and will always trigger.
|
20
|
+
puts "#{__method__}"
|
21
|
+
Resque::StuckQueue.config[:heartbeat_job] = proc { nil } # does not refresh global key
|
22
|
+
@triggered = false
|
23
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { @triggered = true }
|
24
|
+
start_and_stop_loops_after(3)
|
25
|
+
assert @triggered, "will trigger because global key will be old"
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_will_fail_with_bad_custom_heartbeat_job
|
29
|
+
puts "#{__method__}"
|
30
|
+
begin
|
31
|
+
Resque::StuckQueue.config[:heartbeat_job] = proc { raise 'bad proc doc' } # does not refresh global key
|
32
|
+
@triggered = false
|
33
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { @triggered = true }
|
34
|
+
start_and_stop_loops_after(3)
|
35
|
+
assert false, "should not succeed with bad refresh_job"
|
36
|
+
rescue
|
37
|
+
assert true, "will fail with bad refresh_job"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
data/test/test_ver_2.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# run with
|
2
|
+
# $ RESQUE_2=1 bi; RESQUE_2=1 be ruby -I. -Ilib/ test/test_resque_2.rb
|
3
|
+
if !ENV['RESQUE_2'].nil?
|
4
|
+
|
5
|
+
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
|
6
|
+
|
7
|
+
class TestResque2 < Minitest::Test
|
8
|
+
|
9
|
+
include TestHelper
|
10
|
+
|
11
|
+
def setup
|
12
|
+
assert (Resque::VERSION.match /^2\./), "must run in 2.0"
|
13
|
+
Resque.redis = Redis.new
|
14
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
15
|
+
Redis.new.flushall
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_works_with_2_point_oh_do_not_trigger_because_key_is_updated
|
19
|
+
|
20
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
21
|
+
|
22
|
+
Resque::StuckQueue.config[:watcher_interval] = 1
|
23
|
+
Resque::StuckQueue.config[:heartbeat_interval] = 1
|
24
|
+
Resque::StuckQueue.config[:abort_on_exception] = true
|
25
|
+
Resque::StuckQueue.config[:trigger_timeout] = 5
|
26
|
+
Resque::StuckQueue.config[:logger] = Logger.new($stdout)
|
27
|
+
Resque::StuckQueue.config[:triggered_handler] = proc { Redis.new.incr("test-incr-key") }
|
28
|
+
Resque::StuckQueue.config[:redis] = Redis.new
|
29
|
+
Resque::StuckQueue.config[:queues] = [:app]
|
30
|
+
|
31
|
+
#binding.pry
|
32
|
+
Resque::StuckQueue.start_in_background
|
33
|
+
|
34
|
+
@r2_pid = fork { Resque::StuckQueue.config[:redis] = Redis.new ; Resque::Worker.new("*", :graceful_term => true).work ; Process.waitall }
|
35
|
+
sleep 10
|
36
|
+
|
37
|
+
# triggers once
|
38
|
+
assert_equal Redis.new.get("test-incr-key").to_i, 1
|
39
|
+
hax_kill_resque
|
40
|
+
Resque::StuckQueue.force_stop!
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: resque_stuck_queue_revised
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dave Kerr
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: redis-mutex
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: redis-namespace
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: where the wild things are. err, when resque gets stuck
|
70
|
+
email:
|
71
|
+
- davek09@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- Gemfile
|
78
|
+
- LICENSE.txt
|
79
|
+
- README.md
|
80
|
+
- Rakefile
|
81
|
+
- THOUGHTS
|
82
|
+
- lib/resque/stuck_queue.rb
|
83
|
+
- lib/resque_stuck_queue.rb
|
84
|
+
- lib/resque_stuck_queue/config.rb
|
85
|
+
- lib/resque_stuck_queue/heartbeat_job.rb
|
86
|
+
- lib/resque_stuck_queue/version.rb
|
87
|
+
- resque_stuck_queue.gemspec
|
88
|
+
- test/resque/set_redis_key.rb
|
89
|
+
- test/test_collision.rb
|
90
|
+
- test/test_config.rb
|
91
|
+
- test/test_helper.rb
|
92
|
+
- test/test_integration.rb
|
93
|
+
- test/test_lagtime.rb
|
94
|
+
- test/test_named_queues.rb
|
95
|
+
- test/test_resque_stuck_queue.rb
|
96
|
+
- test/test_set_custom_refresh_job.rb
|
97
|
+
- test/test_ver_2.rb
|
98
|
+
homepage: ''
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - '>='
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.2.2
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: fire a handler when your queues are wonky
|
122
|
+
test_files:
|
123
|
+
- test/resque/set_redis_key.rb
|
124
|
+
- test/test_collision.rb
|
125
|
+
- test/test_config.rb
|
126
|
+
- test/test_helper.rb
|
127
|
+
- test/test_integration.rb
|
128
|
+
- test/test_lagtime.rb
|
129
|
+
- test/test_named_queues.rb
|
130
|
+
- test/test_resque_stuck_queue.rb
|
131
|
+
- test/test_set_custom_refresh_job.rb
|
132
|
+
- test/test_ver_2.rb
|