resque_stuck_queue 0.0.9 → 0.0.10

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
- data.tar.gz: cd305cd673750663559d00b2b6412f19becc6a50
4
- metadata.gz: 27e1e7879f29cb47777bfaf71dc85dac57458812
3
+ data.tar.gz: 984e71835c73d5f071caca892a28cb91ac6efb8f
4
+ metadata.gz: 0f1326d639f3e02c6710b18bb4ba16f25edd30f9
5
5
  SHA512:
6
- data.tar.gz: 2a99f6cb2b8efa736a22b02ef89245b09f58b86e0ed9189623979e8561d59101b5e88c74c6354a8ee4f26b8a66d5c9e5473a1c471ee4daa893f57136c2f3d8b6
7
- metadata.gz: 08d93e4f41bfb98204c7c3cb2c3df9291d65876f11cc339559753be824dfb42f4a79f442eab1e7af0f25daf3647e058cd924172efe0eabc04bd69fb8e8756b80
6
+ data.tar.gz: c21a0aab9167fc5dbf9e9d95f7625165bc306df82f509db512198eefc79cf51a1801a65af658239b44d5959a52ad68406a4527bd14a4b953d4816b2f9eb59f05
7
+ metadata.gz: 7f5674fd8a47fd21f08ba0114aa6f8fde2fb75e0237fa8e9cce5d1462b5cb8be9dcf8427d05c9cb19499a55835d52733c82849e7b60323b06b5d5bacfd05026d
data/README.md CHANGED
@@ -6,7 +6,7 @@ This is to be used to satisfy an ops problem. There have been cases resque proce
6
6
 
7
7
  ## What is it?
8
8
 
9
- If resque doesn't run jobs within a certain timeframe, it will trigger a pre-defined handler of your choice. You can use this to send an email, pager duty, add more resque workers, restart resque, send you a txt...whatever suits you.
9
+ If resque doesn't run jobs in specific queues (defaults to `@queue = :app`) within a certain timeframe, it will trigger a pre-defined handler of your choice. You can use this to send an email, pager duty, add more resque workers, restart resque, send you a txt...whatever suits you.
10
10
 
11
11
  ## How it works
12
12
 
@@ -39,10 +39,11 @@ Resque::StuckQueue.config[:trigger_timeout] = 10.hours
39
39
  Resque::StuckQueue.config[:handler] = proc { send_email }
40
40
 
41
41
  # optional, in case you want to set your own name for the key that will be used as the last good hearbeat time
42
+ # note this will be namespaced under the specific queue it's monitoring, for eg "app:name-the-refresh-key-as-you-please"
42
43
  Resque::StuckQueue.config[:global_key] = "name-the-refresh-key-as-you-please"
43
44
 
44
- # optional, monitor a specific redis queue
45
- Resque::StuckQueue.config[:queue_name] = :app
45
+ # optional, monitor specific queues you want to send a heartbeat/monitor
46
+ Resque::StuckQueue.config[:queues] = [:app, :high, :my_custom_queue_name]
46
47
 
47
48
  # optional, if you want the resque-stuck-queue threads to explicitly raise, default is false
48
49
  Resque::StuckQueue.config[:abort_on_exception] = true
@@ -109,7 +110,7 @@ $ bundle exec rake --trace resque:stuck_queue
109
110
 
110
111
  ## Sidekiq/Other redis-based job queues
111
112
 
112
- If you have trouble with other queues you can use this lib by setting your own custom refresh job (aka, the job that refreshes the global_key). The one thing you need to take care of is ensure whatever and however you enque your own custom job, it sets the global_key to Time.now:
113
+ If you have trouble with other queues you can use this lib by setting your own custom refresh job (aka, the job that refreshes your queue specific global_key). The one thing you need to take care of is ensure whatever and however you enque your own custom job, it sets the global_key to Time.now:
113
114
 
114
115
  <pre>
115
116
 
@@ -117,7 +118,7 @@ class CustomJob
117
118
  include Sidekiq::Worker
118
119
  def perform
119
120
  # ensure you're setting the key in the redis the job queue is using
120
- $redis.set(Resque::StuckQueue.global_key, Time.now.to_i)
121
+ $redis.set(Resque::StuckQueue.global_key_for(queue_name), Time.now.to_i)
121
122
  end
122
123
  end
123
124
 
@@ -1,5 +1,5 @@
1
1
  module Resque
2
2
  module StuckQueue
3
- VERSION = "0.0.9"
3
+ VERSION = "0.0.10"
4
4
  end
5
5
  end
@@ -50,6 +50,18 @@ module Resque
50
50
  @redis ||= (config[:redis] || Resque.redis)
51
51
  end
52
52
 
53
+ def global_key_for(under_queue)
54
+ "#{under_queue}:#{config[:global_key] || GLOBAL_KEY}"
55
+ end
56
+
57
+ def global_keys
58
+ queues.map{|q| global_key_for(q) }
59
+ end
60
+
61
+ def queues
62
+ @queues ||= (config[:queues] || [:app])
63
+ end
64
+
53
65
  def start_in_background
54
66
  Thread.new do
55
67
  Thread.current.abort_on_exception = config[:abort_on_exception]
@@ -94,6 +106,7 @@ module Resque
94
106
  def reset!
95
107
  # clean state so we can stop and start in the same process.
96
108
  @config = config.dup #unfreeze
109
+ @queues = nil
97
110
  @running = false
98
111
  @logger = nil
99
112
  end
@@ -102,15 +115,6 @@ module Resque
102
115
  @stopped
103
116
  end
104
117
 
105
- def global_key
106
- # public, for use in custom heartbeat job
107
- "#{named_queue}:#{config[:global_key] || GLOBAL_KEY}"
108
- end
109
-
110
- def named_queue
111
- config[:named_queue] || :app
112
- end
113
-
114
118
  private
115
119
 
116
120
  def enqueue_repeating_refresh_job
@@ -121,17 +125,20 @@ module Resque
121
125
  # we want to go through resque jobs, because that's what we're trying to test here:
122
126
  # ensure that jobs get executed and the time is updated!
123
127
  logger.info("Sending refresh job")
124
- enqueue_job
128
+ enqueue_jobs
125
129
  wait_for_it
126
130
  end
127
131
  end
128
132
  end
129
133
 
130
- def enqueue_job
134
+ def enqueue_jobs
131
135
  if config[:refresh_job]
136
+ # FIXME config[:refresh_job] with mutliple queues is bad semantics
132
137
  config[:refresh_job].call
133
138
  else
134
- Resque.enqueue(RefreshLatestTimestamp, [global_key, redis.client.host, redis.client.port])
139
+ queues.each do |queue_name|
140
+ Resque.enqueue_to(queue_name, RefreshLatestTimestamp, [global_key_for(queue_name), redis.client.host, redis.client.port])
141
+ end
135
142
  end
136
143
  end
137
144
 
@@ -143,9 +150,11 @@ module Resque
143
150
  mutex = Redis::Mutex.new('resque_stuck_queue_lock', block: 0)
144
151
  if mutex.lock
145
152
  begin
146
- if Time.now.to_i - last_time_worked > max_wait_time
147
- logger.info("Triggering handler at #{Time.now} (pid: #{Process.pid})")
148
- trigger_handler
153
+ queues.each do |queue_name|
154
+ if Time.now.to_i - last_time_worked(queue_name) > max_wait_time
155
+ logger.info("Triggering handler for #{queue_name} at #{Time.now} (pid: #{Process.pid})")
156
+ trigger_handler(queue_name)
157
+ end
149
158
  end
150
159
  ensure
151
160
  mutex.unlock
@@ -156,31 +165,31 @@ module Resque
156
165
  end
157
166
  end
158
167
 
159
- def last_time_worked
160
- time_set = read_from_redis
168
+ def last_time_worked(queue_name)
169
+ time_set = read_from_redis(queue_name)
161
170
  if time_set
162
171
  time_set
163
172
  else
164
- manual_refresh
173
+ manual_refresh(queue_name)
165
174
  end.to_i
166
175
  end
167
176
 
168
- def manual_refresh
177
+ def manual_refresh(queue_name)
169
178
  time = Time.now.to_i
170
- redis.set(global_key, time)
179
+ redis.set(global_key_for(queue_name), time)
171
180
  time
172
181
  end
173
182
 
174
- def trigger_handler
183
+ def trigger_handler(queue_name)
175
184
  (config[:handler] || HANDLER).call
176
- manual_refresh
185
+ manual_refresh(queue_name)
177
186
  rescue => e
178
- logger.info("handler crashed: #{e.inspect}")
187
+ logger.info("handler for #{queue_name} crashed: #{e.inspect}")
179
188
  force_stop!
180
189
  end
181
190
 
182
- def read_from_redis
183
- redis.get(global_key)
191
+ def read_from_redis(queue_name)
192
+ redis.get(global_key_for(queue_name))
184
193
  end
185
194
 
186
195
  def wait_for_it
@@ -195,7 +204,6 @@ module Resque
195
204
  end
196
205
 
197
206
  class RefreshLatestTimestamp
198
- @queue = Resque::StuckQueue.named_queue
199
207
  def self.perform(args)
200
208
  timestamp_key = args[0]
201
209
  host = args[1]
data/test/test_helper.rb CHANGED
@@ -11,8 +11,8 @@ require File.join(File.expand_path(File.dirname(__FILE__)), "resque", "refresh_l
11
11
 
12
12
  module TestHelper
13
13
 
14
- def run_resque
15
- pid = fork { exec("QUEUE=* bundle exec rake --trace resque:work") }
14
+ def run_resque(queue_name = "*")
15
+ pid = fork { exec("QUEUE=#{queue_name} bundle exec rake --trace resque:work") }
16
16
  sleep 3 # wait for resque to boot up
17
17
  pid
18
18
  end
@@ -2,13 +2,16 @@ require 'minitest'
2
2
  require "minitest/autorun"
3
3
  require 'pry'
4
4
 
5
+
5
6
  $:.unshift(".")
6
7
  require 'resque_stuck_queue'
7
8
  require File.join(File.expand_path(File.dirname(__FILE__)), "resque", "set_redis_key")
8
9
  require File.join(File.expand_path(File.dirname(__FILE__)), "resque", "refresh_latest_timestamp")
10
+ require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
9
11
 
10
12
  class TestIntegration < Minitest::Test
11
13
 
14
+ include TestHelper
12
15
  # TODODS there's a better way to do this.
13
16
  #
14
17
  #
@@ -30,12 +33,6 @@ class TestIntegration < Minitest::Test
30
33
  Process.waitpid(@resque_pid)
31
34
  end
32
35
 
33
- def run_resque
34
- pid = fork { exec("QUEUE=* bundle exec rake --trace resque:work") }
35
- sleep 3 # wait for resque to boot up
36
- pid
37
- end
38
-
39
36
  def test_resque_enqueues_a_job_does_not_trigger
40
37
  puts "#{__method__}"
41
38
  Resque::StuckQueue.config[:trigger_timeout] = 100 # wait a while so we don't trigger
@@ -84,7 +81,7 @@ class TestIntegration < Minitest::Test
84
81
  Resque::StuckQueue.config[:heartbeat] = 1
85
82
 
86
83
  begin
87
- Resque::StuckQueue.config[:refresh_job] = proc { Resque.enqueue(RefreshLatestTimestamp, Resque::StuckQueue.global_key) }
84
+ Resque::StuckQueue.config[:refresh_job] = proc { Resque.enqueue(RefreshLatestTimestamp, Resque::StuckQueue.global_key_for(:app)) }
88
85
  @triggered = false
89
86
  Resque::StuckQueue.config[:handler] = proc { @triggered = true }
90
87
  Thread.new { Resque::StuckQueue.start }
@@ -98,4 +95,5 @@ class TestIntegration < Minitest::Test
98
95
  end
99
96
  end
100
97
 
98
+
101
99
  end
@@ -0,0 +1,68 @@
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] = 1
10
+ Resque::StuckQueue.config[:abort_on_exception] = true
11
+ Resque.redis.flushall
12
+ end
13
+
14
+ def teardown
15
+ `kill -9 #{@resque_pid}` if @resque_pid
16
+ Resque::StuckQueue.stop
17
+ Process.waitpid(@resque_pid) if @resque_pid
18
+ end
19
+
20
+ def test_no_custom_queues_defaults_to_app
21
+ puts "#{__method__}"
22
+ Resque::StuckQueue.config[:queues] = nil
23
+ start_and_stop_loops_after(2)
24
+ assert Resque::StuckQueue.global_keys.include?("app:resque-stuck-queue"), 'has global keys'
25
+ end
26
+
27
+ def test_has_custom_queues
28
+ puts "#{__method__}"
29
+ Resque::StuckQueue.config[:queues] = [:foo,:bar]
30
+ start_and_stop_loops_after(2)
31
+ assert Resque::StuckQueue.global_keys.include?("foo:resque-stuck-queue"), 'has global keys'
32
+ end
33
+
34
+ def test_resque_enqueues_a_job_with_resqueue_running_but_on_that_queue_does_trigger
35
+ puts "#{__method__}"
36
+ Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
37
+ Resque::StuckQueue.config[:heartbeat] = 1
38
+ @triggered = false
39
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
40
+ Resque::StuckQueue.start_in_background
41
+
42
+ # job gets enqueued successfully
43
+ @resque_pid = run_resque("no-such-jobs-for-this-queue")
44
+ sleep 2 # allow timeout to trigger
45
+
46
+ # check handler did get called
47
+ assert_equal @triggered, true
48
+ end
49
+
50
+ def test_resque_enqueues_a_job_correct_queue_does_not_trigger
51
+ puts "#{__method__}"
52
+ Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
53
+ Resque::StuckQueue.config[:heartbeat] = 1
54
+ Resque::StuckQueue.config[:queues] = [:custom_queue_name, :diff_one]
55
+ assert Resque::StuckQueue.global_keys.include?("custom_queue_name:resque-stuck-queue"), 'has global keys'
56
+ @triggered = false
57
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
58
+ @resque_pid = run_resque("custom_queue_name")
59
+ Resque::StuckQueue.start_in_background
60
+ sleep 2 # allow timeout to trigger
61
+
62
+ # check handler did not get called
63
+ assert_equal @triggered, false
64
+ end
65
+
66
+ end
67
+
68
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: resque_stuck_queue
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shai Rosenfeld
@@ -66,7 +66,7 @@ files:
66
66
  - test/test_helper.rb
67
67
  - test/test_integration.rb
68
68
  - test/test_logger.rb
69
- - test/test_named_queue.rb
69
+ - test/test_named_queues.rb
70
70
  - test/test_resque_2.rb
71
71
  - test/test_resque_stuck_queue.rb
72
72
  - test/test_set_custom_refresh_job.rb
@@ -100,7 +100,7 @@ test_files:
100
100
  - test/test_helper.rb
101
101
  - test/test_integration.rb
102
102
  - test/test_logger.rb
103
- - test/test_named_queue.rb
103
+ - test/test_named_queues.rb
104
104
  - test/test_resque_2.rb
105
105
  - test/test_resque_stuck_queue.rb
106
106
  - test/test_set_custom_refresh_job.rb
@@ -1,35 +0,0 @@
1
- require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
2
-
3
- class TestNamedQueue < Minitest::Test
4
-
5
- include TestHelper
6
-
7
- def setup
8
- Resque::StuckQueue.config[:trigger_timeout] = 1
9
- Resque::StuckQueue.config[:heartbeat] = 1
10
- Resque::StuckQueue.config[:abort_on_exception] = true
11
- end
12
-
13
- def teardown
14
- Resque::StuckQueue.reset!
15
- end
16
-
17
- def test_no_custom_named_queue
18
- puts "#{__method__}"
19
- Resque::StuckQueue.config[:named_queue] = nil
20
- start_and_stop_loops_after(2)
21
- assert_equal Resque::StuckQueue.global_key, "app:resque-stuck-queue"
22
- assert_equal Resque::StuckQueue.named_queue, :app
23
- end
24
-
25
- def test_has_custom_named_queue
26
- puts "#{__method__}"
27
- Resque::StuckQueue.config[:named_queue] = :foo
28
- start_and_stop_loops_after(2)
29
- assert_equal Resque::StuckQueue.global_key, "foo:resque-stuck-queue"
30
- assert_equal Resque::StuckQueue.named_queue, :foo
31
- end
32
-
33
- end
34
-
35
-