resque_stuck_queue 0.0.1

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 ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Zjg2N2FjNjcwNjA2MTZiNTQ1NGJjZjQwZmNiZjI5YzcyYjcxZDdmYw==
5
+ data.tar.gz: !binary |-
6
+ ZDAxNGUwYzUxYTg5NmY2N2Q0OGQ0ZTI0MzQ2MzVlNmJiNDJjY2I3OA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MWMxODVkOTM5NGJlMGQwZWQ5YWU1YTNjN2Q0NWM1NGUzYmExZjBmZjFkN2Yx
10
+ MjA5MGIzNDliY2YwYzU5NGEwNTY4NmYxMDNhZGY5MWNkYjU0ZGM1MDhjZmNk
11
+ ZGU4YzU1NGM2ZTc5Y2Y0OTIyYWU0NGE0OTQwNmM1ZjdmZTZhZWM=
12
+ data.tar.gz: !binary |-
13
+ M2VmNmZmY2YwMDExNzdlYzZjM2ZhMWUxZTM4NDViNjBjYjc2NmYzOWI1NDkz
14
+ MjRjNmZlNmM1MjA0MGJhYzAzNWUyNWIwMjVhMGVjZTFhNjM4MjkzMDA0NjQx
15
+ Yjg2M2VmZDU3ZWM0MWUyODFlNTI1YWVmOWQ4NjlhNzNmM2Y0NWU=
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *gem
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'resque'
4
+
5
+ # TEST
6
+ gem 'minitest'
7
+ gem 'mocha'
8
+ gem 'resque-mock'
9
+ gem 'pry'
10
+ gem 'rake'
11
+ gem 'm'
12
+ gem 'resque-scheduler'
data/Gemfile.lock ADDED
@@ -0,0 +1,61 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ coderay (1.1.0)
5
+ m (1.3.2)
6
+ method_source (>= 0.6.7)
7
+ rake (>= 0.9.2.2)
8
+ metaclass (0.0.1)
9
+ method_source (0.8.2)
10
+ minitest (5.2.0)
11
+ mocha (0.14.0)
12
+ metaclass (~> 0.0.1)
13
+ mono_logger (1.1.0)
14
+ multi_json (1.8.2)
15
+ pry (0.9.12.4)
16
+ coderay (~> 1.0)
17
+ method_source (~> 0.8)
18
+ slop (~> 3.4)
19
+ rack (1.5.2)
20
+ rack-protection (1.5.1)
21
+ rack
22
+ rake (10.1.0)
23
+ redis (3.0.6)
24
+ redis-namespace (1.4.1)
25
+ redis (~> 3.0.4)
26
+ resque (1.25.1)
27
+ mono_logger (~> 1.0)
28
+ multi_json (~> 1.0)
29
+ redis-namespace (~> 1.2)
30
+ sinatra (>= 0.9.2)
31
+ vegas (~> 0.1.2)
32
+ resque-mock (0.1.1)
33
+ resque
34
+ resque-scheduler (2.0.1)
35
+ redis (>= 2.0.1)
36
+ resque (>= 1.20.0)
37
+ rufus-scheduler
38
+ rufus-scheduler (2.0.19)
39
+ tzinfo (>= 0.3.23)
40
+ sinatra (1.4.4)
41
+ rack (~> 1.4)
42
+ rack-protection (~> 1.4)
43
+ tilt (~> 1.3, >= 1.3.4)
44
+ slop (3.4.7)
45
+ tilt (1.4.1)
46
+ tzinfo (0.3.38)
47
+ vegas (0.1.11)
48
+ rack (>= 1.0.0)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ m
55
+ minitest
56
+ mocha
57
+ pry
58
+ rake
59
+ resque
60
+ resque-mock
61
+ resque-scheduler
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Shai Rosenfeld
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ ## Resque stuck queue
2
+
3
+ Ever run into that? Sucks, eh?
4
+
5
+ This should enable a way to fire some handler when jobs aren't occurring within a certain timeframe.
6
+
7
+ ## How it works
8
+
9
+ When you call `start` you are essentially starting two threads that will continiously run until `stop` is called or until the process shuts down.
10
+
11
+ One thread is responsible for pushing a 'heartbeat' job to resque which will essentially refresh a specific key in redis every time that job is processed.
12
+
13
+ The other thread is a continious loop that will check redis (bypassing resque) for that key and check what the latest time the hearbeat job successfully updated that key.
14
+
15
+ It will trigger a pre-defined proc (see below) if the last time the hearbeat job updated that key is older than the trigger_timeout setting (see below).
16
+
17
+ ## Usage
18
+
19
+ Add this to wherever you're setting up resque (config/initializers or wherever).
20
+
21
+ Configure it first:
22
+
23
+ <pre>
24
+ # how often to push that 'heartbeat' job to refresh the latest time it worked.
25
+ Resque::StuckQueue.config[:heartbeat] = 5.minutes
26
+
27
+ # since there is an realistic and acceptable lag for job queues, set this to how much you're
28
+ # willing to accept between the current time and when the last hearbeat job went through.
29
+ #
30
+ # obviously, take the heartbeat into consideration when setting this
31
+ Resque::StuckQueue.config[:trigger_timeout] = 10.hours
32
+
33
+ # what gets triggered when resque-stuck-queue will detect the latest heartbeat is older than the trigger_timeout time set above.
34
+ Resque::StuckQueue.config[:handler] = proc { send_email }
35
+
36
+ # optional, in case you want to set your own name for the key that will be used as the last good hearbeat time
37
+ Resque::StuckQueue.config[:global_key] = "name-the-refresh-key-as-you-please"
38
+
39
+ # optional, if you want the resque-stuck-queue threads to explicitly raise, default is false
40
+ Resque::StuckQueue.config[:abort_on_exception] = true
41
+ </pre>
42
+
43
+ Then start it:
44
+
45
+ <pre>
46
+ Resque::StuckQueue.start # blocking
47
+ Resque::StuckQueue.start_in_background # sugar for Thread.new { Resque::StuckQueue.start }
48
+ </pre>
49
+
50
+ Stopping it consists of the same idea:
51
+
52
+ <pre>
53
+ Resque::StuckQueue.stop # this will block until the threads end their current iteration
54
+ Resque::StuckQueue.force_stop! # force kill those threads and let's move on
55
+ </pre>
56
+
57
+ ## Tests
58
+
59
+ Run the tests:
60
+
61
+ `bundle; bundle exec rake`
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require 'rake/testtask'
2
+
3
+ task :default => :test
4
+ Rake::TestTask.new do |t|
5
+ t.pattern = "test/test_*rb"
6
+ end
7
+
8
+ require 'resque/tasks'
9
+
10
+ task :'resque:setup' do
11
+ # https://github.com/resque/resque/issues/773
12
+ # have the jobs loaded in memory
13
+ Dir["./test/resque/*.rb"].each {|file| require file}
14
+ end
15
+
16
+ require 'resque_scheduler/tasks'
17
+ task "resque:scheduler_setup"
18
+
data/THOUGHTS ADDED
@@ -0,0 +1,21 @@
1
+ other resources:
2
+
3
+ http://vitobotta.com/resque-automatically-kill-stuck-workers-retry-failed-jobs/#sthash.oQsaNeb5.dpbs
4
+ http://stackoverflow.com/questions/10757758/find-out-if-a-resque-job-is-still-running-and-kill-it-if-its-stuck
5
+
6
+ heartbeat?
7
+
8
+ redis scenario
9
+ different processes (cant share state in Resque memory etc.)
10
+ different boxes! (redis is shared state)
11
+ if this is included in servers, you have clusters os unicorn/mongrel etc,
12
+ ensure it works across multiple running instances of this lib?
13
+
14
+ Thing.setup_checker_thread
15
+ Thing.enqueue_repeating_refresh_job
16
+
17
+ reproduce the error and integrate test like that
18
+
19
+ ## TODOS
20
+
21
+ verify @config options, raise if no handler, etc.
@@ -0,0 +1,5 @@
1
+ module Resque
2
+ module StuckQueue
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,157 @@
1
+ require "resque_stuck_queue/version"
2
+
3
+ # TODO move this require into a configurable?
4
+ require 'resque'
5
+
6
+ module Resque
7
+ module StuckQueue
8
+
9
+ GLOBAL_KEY = "resque-stuck-queue"
10
+ VERIFIED_KEY = "resque-stuck-queue-ran-once"
11
+ HEARTBEAT = 60 * 60 # check/refresh every hour
12
+ TRIGGER_TIMEOUT = 5 * 60 * 60 # warn/trigger 5 hours
13
+ HANDLER = proc { $stderr.puts("Shit gone bad with them queues.") }
14
+
15
+ class << self
16
+
17
+ attr_accessor :config
18
+
19
+ # # how often we refresh the key
20
+ # :heartbeat = 5 * 60
21
+ #
22
+ # # this could just be :heartbeat but it's possible there's an acceptable lag/bottleneck
23
+ # # in the queue that we want to allow to be before we think it's bad.
24
+ # :trigger_timeout = 10 * 60
25
+ #
26
+ # # The global key that will be used to check the latest time
27
+ # :global_key = "resque-stuck-queue"
28
+ #
29
+ # # for threads involved here. default is false
30
+ # :abort_on_exception
31
+ #
32
+ # # default handler
33
+ # config[:handler] = proc { send_mail }
34
+ def config
35
+ @config ||= {}
36
+ end
37
+
38
+ def start_in_background
39
+ Thread.new do
40
+ self.start
41
+ end
42
+ end
43
+
44
+ def stop_in_background
45
+ Thread.new do
46
+ self.start
47
+ end
48
+ end
49
+
50
+ # call this after setting config. once started you should't be allowed to modify it
51
+ def start
52
+ @running = true
53
+ @stopped = false
54
+ @threads = []
55
+ config.freeze
56
+
57
+ mark_first_use
58
+
59
+ Thread.abort_on_exception = config[:abort_on_exception]
60
+
61
+ enqueue_repeating_refresh_job
62
+ setup_checker_thread
63
+
64
+ # fo-eva.
65
+ @threads.map(&:join)
66
+
67
+ @stopped = true
68
+ end
69
+
70
+ # for tests
71
+ def stop
72
+ @config = config.dup #unfreeze
73
+ @running = false
74
+
75
+ # wait for clean thread shutdown
76
+ while @stopped == false
77
+ sleep 1
78
+ end
79
+ end
80
+
81
+ def force_stop!
82
+ @threads.map(&:kill)
83
+ end
84
+
85
+ private
86
+
87
+ def enqueue_repeating_refresh_job
88
+ @threads << Thread.new do
89
+ while @running
90
+ wait_for_it
91
+ # we want to go through resque jobs, because that's what we're trying to test here:
92
+ # ensure that jobs get executed and the time is updated!
93
+ #
94
+ # TODO REDIS 2.0 compat
95
+ Resque.enqueue(RefreshLatestTimestamp, global_key)
96
+ end
97
+ end
98
+ end
99
+
100
+ def setup_checker_thread
101
+ @threads << Thread.new do
102
+ while @running
103
+ wait_for_it
104
+ if Time.now.to_i - last_time_worked > max_wait_time
105
+ trigger_handler
106
+ end
107
+ end
108
+ end
109
+ end
110
+
111
+ def last_time_worked
112
+ time_set = read_from_redis
113
+ if has_been_used? && time_set.nil?
114
+ # if the first job ran, the redis key should always be set
115
+ # possible cases are (1) redis data wonky (2) resque jobs don't get run
116
+ trigger_handler
117
+ end
118
+ (time_set ? time_set : Time.now).to_i # don't trigger again if time is nil
119
+ end
120
+
121
+ def trigger_handler
122
+ (config[:handler] || HANDLER).call
123
+ end
124
+
125
+ def read_from_redis
126
+ Resque.redis.get(global_key)
127
+ end
128
+
129
+ def has_been_used?
130
+ Resque.redis.get(VERIFIED_KEY)
131
+ end
132
+
133
+ def mark_first_use
134
+ Resque.redis.set(VERIFIED_KEY, "true")
135
+ end
136
+
137
+ def wait_for_it
138
+ sleep config[:heartbeat] || HEARTBEAT
139
+ end
140
+
141
+ def global_key
142
+ config[:global_key] || GLOBAL_KEY
143
+ end
144
+
145
+ def max_wait_time
146
+ config[:trigger_timeout] || TRIGGER_TIMEOUT
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ class RefreshLatestTimestamp
153
+ @queue = :app
154
+ def self.perform(timestamp_key)
155
+ Resque.redis.set(timestamp_key, Time.now.to_i)
156
+ end
157
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'resque_stuck_queue/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "resque_stuck_queue"
8
+ spec.version = Resque::StuckQueue::VERSION
9
+ spec.authors = ["Shai Rosenfeld"]
10
+ spec.email = ["srosenfeld@engineyard.com"]
11
+ spec.summary = %q{fire a handler when your queues are wonky}
12
+ spec.description = %q{where the wild things are. err, when resque gets stuck}
13
+ spec.homepage = "https://github.com/shaiguitar/resque_stuck_queue/"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.5"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,6 @@
1
+ class RefreshLatestTimestamp
2
+ @queue = :app
3
+ def self.perform(timestamp_key)
4
+ Resque.redis.set(timestamp_key, Time.now.to_i)
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ # fixture job
2
+ class SetRedisKey
3
+ NAME = "integration_test"
4
+ @queue = :app
5
+ def self.perform
6
+ Resque.redis.set(NAME, "1")
7
+ end
8
+ end
@@ -0,0 +1,83 @@
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__)), "resque", "refresh_latest_timestamp")
9
+
10
+ class TestIntegration < Minitest::Test
11
+
12
+ # TODODS there's a better way to do this.
13
+ #
14
+ #
15
+ # run test with VVERBOSE=1 DEBUG=1 for more output
16
+ #
17
+ #
18
+ # => sleeping suckc
19
+ # => resque sleeps 5 between checking enqueed jobs, can be configurable?
20
+ #
21
+ # cleanup processes correctly?
22
+ # ps aux |grep resqu |awk '{print $2}' | xargs kill
23
+
24
+ def setup
25
+ end
26
+
27
+ def teardown
28
+ Process.kill("SIGQUIT", @resque_pid)
29
+ Resque::StuckQueue.stop
30
+ Process.waitpid(@resque_pid)
31
+ end
32
+
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
+ def test_resque_enqueues_a_job_does_not_trigger
40
+
41
+ puts '1'
42
+ Resque::StuckQueue.config[:trigger_timeout] = 100 # wait a while so we don't trigger
43
+ Resque::StuckQueue.config[:heartbeat] = 2
44
+ @triggered = false
45
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
46
+ Thread.new { Resque::StuckQueue.start }
47
+
48
+ # job gets enqueued successfully
49
+ @resque_pid = run_resque
50
+ Resque.redis.del(SetRedisKey::NAME)
51
+ Resque.enqueue(SetRedisKey)
52
+ sleep 6 # let resque pick up the job
53
+ assert_equal Resque.redis.get(SetRedisKey::NAME), "1"
54
+
55
+ # check handler did not get called
56
+ assert_equal @triggered, false
57
+ end
58
+
59
+ def test_resque_does_not_enqueues_a_job_does_trigger
60
+
61
+ puts '2'
62
+ Resque::StuckQueue.config[:trigger_timeout] = 2 # won't allow waiting too much and will complain (eg trigger) sooner than later
63
+ Resque::StuckQueue.config[:heartbeat] = 1
64
+ @triggered = false
65
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
66
+ Thread.new { Resque::StuckQueue.start }
67
+
68
+ # job gets enqueued successfully
69
+ @resque_pid = run_resque
70
+ Resque.redis.del(SetRedisKey::NAME)
71
+ Process.kill("SIGSTOP", @resque_pid) # jic, do not process jobs so we definitely trigger
72
+ Resque.enqueue(SetRedisKey)
73
+ assert_equal Resque.redis.get(SetRedisKey::NAME), nil
74
+
75
+ # check handler did get called
76
+ assert_equal @triggered, true
77
+
78
+ # unstick the process so we can kill it in teardown
79
+ Process.kill("SIGCONT", @resque_pid)
80
+ end
81
+
82
+
83
+ end
@@ -0,0 +1,105 @@
1
+ require 'minitest'
2
+ require "minitest/autorun"
3
+ require 'mocha'
4
+
5
+ require 'resque/mock'
6
+
7
+ $:.unshift(".")
8
+ require 'resque_stuck_queue'
9
+
10
+ class TestResqueStuckQueue < Minitest::Test
11
+
12
+ def teardown
13
+ puts 'teardown'
14
+ Resque::StuckQueue.unstub(:has_been_used?)
15
+ Resque::StuckQueue.unstub(:read_from_redis)
16
+ end
17
+
18
+ def setup
19
+ puts 'setup'
20
+ # clean previous test runs
21
+ Resque.redis.flushall
22
+ Resque.mock!
23
+ Resque::StuckQueue.config[:heartbeat] = 1 # seconds
24
+ Resque::StuckQueue.config[:trigger_timeout] = 2
25
+ Resque::StuckQueue.config[:abort_on_exception] = true
26
+ end
27
+
28
+ # usually the key will be set from previous runs since it will persist (redis) between deploys etc.
29
+ # so you shouldn't be running into this scenario (nil key) other than
30
+ # 0) test setup clearing out this key
31
+ # 1) the VERY first time you use this lib when it first gets set.
32
+ # 2) redis gets wiped out
33
+ # 3) resque jobs never get run!
34
+ # this has the unfortunate meaning that if no jobs are *ever* enqueued, this lib won't catch that problem.
35
+ # so we split the funcationaliy to raise if no key is there, unless it's the first time it's being used since being started.
36
+ def test_thread_does_not_trigger_when_no_key_exists_on_first_use
37
+ puts '1'
38
+
39
+ # lib never ran, and key is not there
40
+ Resque::StuckQueue.stubs(:has_been_used?).returns(nil)
41
+ Resque::StuckQueue.stubs(:read_from_redis).returns(nil)
42
+ @triggered = false
43
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
44
+ start_and_stop_loops_after(2)
45
+ assert_equal false, @triggered # "handler should not be called"
46
+ end
47
+
48
+ def test_thread_does_trigger_when_no_key_exists_on_any_other_use
49
+
50
+ puts '2'
51
+ # lib already ran, but key is not there
52
+ Resque::StuckQueue.stubs(:has_been_used?).returns(true)
53
+ Resque::StuckQueue.stubs(:read_from_redis).returns(nil)
54
+
55
+ @triggered = false
56
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
57
+ start_and_stop_loops_after(2)
58
+ assert_equal true, @triggered # "handler should be called"
59
+ end
60
+
61
+ def test_configure_global_key
62
+ puts '3'
63
+ assert_nil Resque.redis.get("it-is-configurable"), "global key should not be set"
64
+ Resque::StuckQueue.config[:global_key] = "it-is-configurable"
65
+ start_and_stop_loops_after(2)
66
+ refute_nil Resque.redis.get("it-is-configurable"), "global key should be set"
67
+ end
68
+
69
+ def test_it_sets_a_verified_key_to_indicate_first_use
70
+ puts '4'
71
+ assert_nil Resque.redis.get(Resque::StuckQueue::VERIFIED_KEY), "should be nil before lib is used"
72
+ start_and_stop_loops_after(2)
73
+ refute_nil Resque.redis.get(Resque::StuckQueue::VERIFIED_KEY), "should set verified key after used"
74
+ end
75
+
76
+ def test_it_does_not_trigger_handler_if_under_max_time
77
+ puts '5'
78
+ Resque::StuckQueue.stubs(:read_from_redis).returns(Time.now.to_i)
79
+ @triggered = false
80
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
81
+ start_and_stop_loops_after(2)
82
+ assert_equal false, @triggered # "handler should not be called"
83
+ end
84
+
85
+ def test_it_triggers_handler_if_over_trigger_timeout
86
+ puts '6'
87
+ last_time_too_old = Time.now.to_i - Resque::StuckQueue::TRIGGER_TIMEOUT
88
+ Resque::StuckQueue.stubs(:read_from_redis).returns(last_time_too_old.to_s)
89
+ @triggered = false
90
+ Resque::StuckQueue.config[:handler] = proc { @triggered = true }
91
+ start_and_stop_loops_after(2)
92
+ assert_equal true, @triggered # "handler should be called"
93
+ end
94
+
95
+ private
96
+
97
+ def start_and_stop_loops_after(secs)
98
+ ops = []
99
+ ops << Thread.new { Resque::StuckQueue.start }
100
+ ops << Thread.new { sleep secs; Resque::StuckQueue.stop }
101
+ ops.map(&:join)
102
+ end
103
+
104
+ end
105
+
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: resque_stuck_queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Shai Rosenfeld
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-01-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: where the wild things are. err, when resque gets stuck
42
+ email:
43
+ - srosenfeld@engineyard.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - .gitignore
49
+ - Gemfile
50
+ - Gemfile.lock
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - THOUGHTS
55
+ - lib/resque_stuck_queue.rb
56
+ - lib/resque_stuck_queue/version.rb
57
+ - resque_stuck_queue.gemspec
58
+ - test/resque/refresh_latest_timestamp.rb
59
+ - test/resque/set_redis_key.rb
60
+ - test/test_integration.rb
61
+ - test/test_resque_stuck_queue.rb
62
+ homepage: https://github.com/shaiguitar/resque_stuck_queue/
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ! '>='
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 2.0.3
83
+ signing_key:
84
+ specification_version: 4
85
+ summary: fire a handler when your queues are wonky
86
+ test_files:
87
+ - test/resque/refresh_latest_timestamp.rb
88
+ - test/resque/set_redis_key.rb
89
+ - test/test_integration.rb
90
+ - test/test_resque_stuck_queue.rb
91
+ has_rdoc: