resque_stuck_queue 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
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
-