sidekiq-recycler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile +17 -0
- data/Gemfile.lock +107 -0
- data/README.md +34 -0
- data/Rakefile +33 -0
- data/VERSION +1 -0
- data/lib/sidekiq/recycler.rb +78 -0
- data/sidekiq-recycler.gemspec +69 -0
- data/test/helper.rb +22 -0
- data/test/support/create_jobs.rb +9 -0
- data/test/support/sidekiq_mock_fetcher.rb +91 -0
- data/test/support/workers.rb +40 -0
- data/test/test_recycler.rb +62 -0
- metadata +154 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: eb953e946bdabfda52012df96add214084291736
|
4
|
+
data.tar.gz: 74e8809c6ae424ba44927c0c84509a7784bd1066
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 558a89279d337b8f4d9fea71c9e561b49e24a509bf35500e64f26ae54144e9e5e062757e9e331e67732d4097e8a82de8a6338a304b2ef685fd8885b5b4312385
|
7
|
+
data.tar.gz: e86bb903e9188e6ce52e1df1706841d96d27491a4433977ef20b3a8d45d48c9299c9e783a4e794ba2655124db90e7be42176579b152d713829256d40c2865891
|
data/Gemfile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
source "https://rubygems.org"
|
3
|
+
|
4
|
+
gem "sidekiq"
|
5
|
+
|
6
|
+
group :local do
|
7
|
+
gem "sidekiq-recycler", :path => "."
|
8
|
+
end
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem "rake"
|
12
|
+
gem "jeweler"
|
13
|
+
gem "yard"
|
14
|
+
gem "logging"
|
15
|
+
gem "easycov"
|
16
|
+
gem "micron", :github => "chetan/micron"
|
17
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
GIT
|
2
|
+
remote: git://github.com/chetan/micron.git
|
3
|
+
revision: d722ad58ca390cdaf01d27d77aea1fbe951d0bca
|
4
|
+
specs:
|
5
|
+
micron (0.1.0)
|
6
|
+
colorize
|
7
|
+
easycov
|
8
|
+
hitimes
|
9
|
+
|
10
|
+
PATH
|
11
|
+
remote: .
|
12
|
+
specs:
|
13
|
+
sidekiq-recycler (0.1.0)
|
14
|
+
sidekiq
|
15
|
+
|
16
|
+
GEM
|
17
|
+
remote: https://rubygems.org/
|
18
|
+
specs:
|
19
|
+
addressable (2.3.5)
|
20
|
+
builder (3.2.2)
|
21
|
+
celluloid (0.15.2)
|
22
|
+
timers (~> 1.1.0)
|
23
|
+
colorize (0.6.0)
|
24
|
+
connection_pool (1.1.0)
|
25
|
+
easycov (0.3.1)
|
26
|
+
multi_json
|
27
|
+
simplecov
|
28
|
+
simplecov-console
|
29
|
+
simplecov-html
|
30
|
+
faraday (0.8.8)
|
31
|
+
multipart-post (~> 1.2.0)
|
32
|
+
git (1.2.6)
|
33
|
+
github_api (0.10.1)
|
34
|
+
addressable
|
35
|
+
faraday (~> 0.8.1)
|
36
|
+
hashie (>= 1.2)
|
37
|
+
multi_json (~> 1.4)
|
38
|
+
nokogiri (~> 1.5.2)
|
39
|
+
oauth2
|
40
|
+
hashie (2.0.5)
|
41
|
+
highline (1.6.19)
|
42
|
+
hirb (0.7.1)
|
43
|
+
hitimes (1.2.1)
|
44
|
+
httpauth (0.2.0)
|
45
|
+
jeweler (1.8.8)
|
46
|
+
builder
|
47
|
+
bundler (~> 1.0)
|
48
|
+
git (>= 1.2.5)
|
49
|
+
github_api (= 0.10.1)
|
50
|
+
highline (>= 1.6.15)
|
51
|
+
nokogiri (= 1.5.10)
|
52
|
+
rake
|
53
|
+
rdoc
|
54
|
+
json (1.8.0)
|
55
|
+
jwt (0.1.8)
|
56
|
+
multi_json (>= 1.5)
|
57
|
+
little-plugger (1.1.3)
|
58
|
+
logging (1.8.1)
|
59
|
+
little-plugger (>= 1.1.3)
|
60
|
+
multi_json (>= 1.3.6)
|
61
|
+
multi_json (1.8.1)
|
62
|
+
multi_xml (0.5.5)
|
63
|
+
multipart-post (1.2.0)
|
64
|
+
nokogiri (1.5.10)
|
65
|
+
oauth2 (0.9.2)
|
66
|
+
faraday (~> 0.8)
|
67
|
+
httpauth (~> 0.2)
|
68
|
+
jwt (~> 0.1.4)
|
69
|
+
multi_json (~> 1.0)
|
70
|
+
multi_xml (~> 0.5)
|
71
|
+
rack (~> 1.2)
|
72
|
+
rack (1.5.2)
|
73
|
+
rake (10.1.0)
|
74
|
+
rdoc (4.0.1)
|
75
|
+
json (~> 1.4)
|
76
|
+
redis (3.0.5)
|
77
|
+
redis-namespace (1.3.1)
|
78
|
+
redis (~> 3.0.0)
|
79
|
+
sidekiq (2.15.1)
|
80
|
+
celluloid (>= 0.15.1)
|
81
|
+
connection_pool (>= 1.0.0)
|
82
|
+
json
|
83
|
+
redis (>= 3.0.4)
|
84
|
+
redis-namespace (>= 1.3.1)
|
85
|
+
simplecov (0.7.1)
|
86
|
+
multi_json (~> 1.0)
|
87
|
+
simplecov-html (~> 0.7.1)
|
88
|
+
simplecov-console (0.1.3)
|
89
|
+
colorize
|
90
|
+
hirb
|
91
|
+
simplecov
|
92
|
+
simplecov-html (0.7.1)
|
93
|
+
timers (1.1.0)
|
94
|
+
yard (0.8.7.2)
|
95
|
+
|
96
|
+
PLATFORMS
|
97
|
+
ruby
|
98
|
+
|
99
|
+
DEPENDENCIES
|
100
|
+
easycov
|
101
|
+
jeweler
|
102
|
+
logging
|
103
|
+
micron!
|
104
|
+
rake
|
105
|
+
sidekiq
|
106
|
+
sidekiq-recycler!
|
107
|
+
yard
|
data/README.md
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Sidekiq Recycler
|
2
|
+
Gracefully recycle sidekiq processes that use too much memory
|
3
|
+
|
4
|
+
sidekiq-recycler is a simple middleware which checks the process's RSS usage on
|
5
|
+
the completion of each job. When the usage surpasses a predefined limit, the process
|
6
|
+
will gracefully terminate. If any jobs are still running beyond a further time threshold,
|
7
|
+
they will be killed and requeued.
|
8
|
+
|
9
|
+
|
10
|
+
## Quickstart
|
11
|
+
|
12
|
+
```
|
13
|
+
$ gem install sidekiq-recycler
|
14
|
+
```
|
15
|
+
|
16
|
+
```ruby
|
17
|
+
# Add the middleware
|
18
|
+
require "sidekiq"
|
19
|
+
require "sidekiq/recycler"
|
20
|
+
|
21
|
+
Sidekiq.configure_server do |config|
|
22
|
+
config.server_middleware do |chain|
|
23
|
+
chain.add Sidekiq::Recycler, :mem_limit => 300_000, :hard_limit_sec => 300
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
|
29
|
+
## Configuration
|
30
|
+
|
31
|
+
Two options are exposed by the middleware:
|
32
|
+
|
33
|
+
* mem_limit: RSS usage limit, in megabytes
|
34
|
+
* hard_limit_sec: time in seconds to wait for jobs to finish, after graceful shutdown initiated
|
data/Rakefile
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
require 'jeweler'
|
14
|
+
|
15
|
+
Jeweler::Tasks.new do |gemspec|
|
16
|
+
gemspec.name = "sidekiq-recycler"
|
17
|
+
gemspec.summary = "Recycle large sidekiq processes"
|
18
|
+
gemspec.description = "Gracefully recycle sidekiq processes that use too much memory"
|
19
|
+
gemspec.email = "chetan@pixelcop.net"
|
20
|
+
gemspec.homepage = "http://github.com/chetan/sidekiq-recycler"
|
21
|
+
gemspec.authors = ["Chetan Sarva"]
|
22
|
+
gemspec.license = "MIT"
|
23
|
+
end
|
24
|
+
Jeweler::RubygemsDotOrgTasks.new
|
25
|
+
|
26
|
+
Dir['tasks/**/*.rake'].each { |rake| load rake }
|
27
|
+
|
28
|
+
task :default => :test
|
29
|
+
|
30
|
+
require 'yard'
|
31
|
+
YARD::Rake::YardocTask.new
|
32
|
+
|
33
|
+
require "easycov/rake"
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,78 @@
|
|
1
|
+
|
2
|
+
module Sidekiq
|
3
|
+
class Recycler
|
4
|
+
|
5
|
+
# used to avoid race conditions when recycling
|
6
|
+
@@mutex = Mutex.new
|
7
|
+
|
8
|
+
# avoid extra spam after hard limit reached
|
9
|
+
@@recycled = false
|
10
|
+
|
11
|
+
def initialize(opts={})
|
12
|
+
@mem_limit = opts[:mem_limit] || 300_000 # default is 300mb
|
13
|
+
@hard_limit_sec = opts[:hard_limit_sec] || 300 # default to 300 sec
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(worker, job, queue)
|
17
|
+
begin
|
18
|
+
yield
|
19
|
+
|
20
|
+
ensure
|
21
|
+
# check mem usage here
|
22
|
+
rss = `ps -o rss= -p #{$$}`.to_i
|
23
|
+
if rss > @mem_limit then
|
24
|
+
|
25
|
+
# handle race conditions with many jobs/threads completing
|
26
|
+
# at the same time
|
27
|
+
return if !@@mutex.try_lock or @@recycled
|
28
|
+
@@recycled = true
|
29
|
+
|
30
|
+
Sidekiq.logger.warn "Recycler threshold reached: #{rss} > #{@mem_limit}"
|
31
|
+
Sidekiq.logger.warn "Attempting to stop gracefully"
|
32
|
+
|
33
|
+
# soft_limit_sec = @soft_limit_sec
|
34
|
+
hard_limit_sec = @hard_limit_sec
|
35
|
+
launcher = nil
|
36
|
+
|
37
|
+
Thread.new do
|
38
|
+
Celluloid::Actor.all.each do |actor|
|
39
|
+
# tell sidekiq to exit gracefully
|
40
|
+
# stops accepting new work and kills all waiting ("ready") threads
|
41
|
+
if actor.kind_of? Sidekiq::Launcher then
|
42
|
+
launcher = actor
|
43
|
+
Thread.new do
|
44
|
+
actor.manager.async.stop
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
Thread.new do
|
51
|
+
# wait until all threads have exited
|
52
|
+
while true do
|
53
|
+
sleep 1
|
54
|
+
next if launcher.nil?
|
55
|
+
if launcher.manager.ready.empty? and launcher.manager.busy.empty? then
|
56
|
+
Sidekiq.logger.info "All threads stopped; exiting now!"
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Thread.new do
|
63
|
+
# wait for hard limit sec then kill
|
64
|
+
sleep hard_limit_sec
|
65
|
+
Sidekiq.logger.warn "Hard limit of #{hard_limit_sec}sec reached; sending TERM signal"
|
66
|
+
if !launcher.nil? then
|
67
|
+
launcher.stop
|
68
|
+
else
|
69
|
+
Process.kill("TERM", $$)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: sidekiq-recycler 0.1.0 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "sidekiq-recycler"
|
9
|
+
s.version = "0.1.0"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = ["Chetan Sarva"]
|
13
|
+
s.date = "2013-10-14"
|
14
|
+
s.description = "Gracefully recycle sidekiq processes that use too much memory"
|
15
|
+
s.email = "chetan@pixelcop.net"
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"README.md"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
"Gemfile",
|
21
|
+
"Gemfile.lock",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"lib/sidekiq/recycler.rb",
|
26
|
+
"sidekiq-recycler.gemspec",
|
27
|
+
"test/helper.rb",
|
28
|
+
"test/support/create_jobs.rb",
|
29
|
+
"test/support/sidekiq_mock_fetcher.rb",
|
30
|
+
"test/support/workers.rb",
|
31
|
+
"test/test_recycler.rb"
|
32
|
+
]
|
33
|
+
s.homepage = "http://github.com/chetan/sidekiq-recycler"
|
34
|
+
s.licenses = ["MIT"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = "2.1.5"
|
37
|
+
s.summary = "Recycle large sidekiq processes"
|
38
|
+
|
39
|
+
if s.respond_to? :specification_version then
|
40
|
+
s.specification_version = 4
|
41
|
+
|
42
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
43
|
+
s.add_runtime_dependency(%q<sidekiq>, [">= 0"])
|
44
|
+
s.add_development_dependency(%q<rake>, [">= 0"])
|
45
|
+
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
46
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
47
|
+
s.add_development_dependency(%q<logging>, [">= 0"])
|
48
|
+
s.add_development_dependency(%q<easycov>, [">= 0"])
|
49
|
+
s.add_development_dependency(%q<micron>, [">= 0"])
|
50
|
+
else
|
51
|
+
s.add_dependency(%q<sidekiq>, [">= 0"])
|
52
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
53
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
54
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
55
|
+
s.add_dependency(%q<logging>, [">= 0"])
|
56
|
+
s.add_dependency(%q<easycov>, [">= 0"])
|
57
|
+
s.add_dependency(%q<micron>, [">= 0"])
|
58
|
+
end
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<sidekiq>, [">= 0"])
|
61
|
+
s.add_dependency(%q<rake>, [">= 0"])
|
62
|
+
s.add_dependency(%q<jeweler>, [">= 0"])
|
63
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
64
|
+
s.add_dependency(%q<logging>, [">= 0"])
|
65
|
+
s.add_dependency(%q<easycov>, [">= 0"])
|
66
|
+
s.add_dependency(%q<micron>, [">= 0"])
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/test/helper.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'bundler'
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
require "logging"
|
13
|
+
require "micron"
|
14
|
+
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
EasyCov.path = "coverage"
|
18
|
+
EasyCov.filters << EasyCov::IGNORE_GEMS << EasyCov::IGNORE_STDLIB
|
19
|
+
EasyCov.filters << lambda { |filename|
|
20
|
+
filename =~ %r(#{EasyCov.root}/test/)
|
21
|
+
}
|
22
|
+
EasyCov.start
|
@@ -0,0 +1,91 @@
|
|
1
|
+
|
2
|
+
require "thread"
|
3
|
+
require "securerandom"
|
4
|
+
|
5
|
+
class MockFetcher
|
6
|
+
|
7
|
+
UnitOfWork = Struct.new(:queue, :message) do
|
8
|
+
def acknowledge
|
9
|
+
end
|
10
|
+
def queue_name
|
11
|
+
"jobs"
|
12
|
+
end
|
13
|
+
def requeue
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :reader, :writer
|
18
|
+
|
19
|
+
def self.instance(opts=nil)
|
20
|
+
if @inst.nil? then
|
21
|
+
@inst = self.allocate
|
22
|
+
@inst.setup
|
23
|
+
end
|
24
|
+
@inst
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
alias_method :new, :instance
|
29
|
+
end
|
30
|
+
|
31
|
+
def setup
|
32
|
+
@reader, @writer = IO.pipe
|
33
|
+
@mutex = Mutex.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def retrieve_work
|
37
|
+
@mutex.synchronize {
|
38
|
+
begin
|
39
|
+
return internal_fetch()
|
40
|
+
rescue Exception => ex
|
41
|
+
$stderr.puts ex
|
42
|
+
end
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.bulk_requeue(*args)
|
47
|
+
# noop
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def internal_fetch
|
54
|
+
|
55
|
+
data = case reader.readline.strip
|
56
|
+
when "forever"
|
57
|
+
{:jid => SecureRandom.hex(4),
|
58
|
+
:class => ForeverWorker.name, :args => []}
|
59
|
+
|
60
|
+
when "hard"
|
61
|
+
{:jid => SecureRandom.hex(4),
|
62
|
+
:class => HardWorker.name, :args => ["test"]}
|
63
|
+
end
|
64
|
+
|
65
|
+
return UnitOfWork.new("foobar", MultiJson.dump(data))
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
module Sidekiq
|
71
|
+
class Fetcher
|
72
|
+
def self.strategy
|
73
|
+
MockFetcher
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
Sidekiq::Logging.logger = Logging.logger[Sidekiq]
|
79
|
+
|
80
|
+
module Sidekiq
|
81
|
+
module Logging
|
82
|
+
def self.with_context(msg)
|
83
|
+
begin
|
84
|
+
::Logging.mdc["sidekiq"] = msg
|
85
|
+
yield
|
86
|
+
ensure
|
87
|
+
::Logging.mdc["sidekiq"] = nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
|
2
|
+
require "sidekiq"
|
3
|
+
require "sidekiq/recycler"
|
4
|
+
|
5
|
+
Sidekiq.configure_server do |config|
|
6
|
+
config.server_middleware do |chain|
|
7
|
+
chain.add Sidekiq::Recycler, :mem_limit => 100_000, :hard_limit_sec => 30
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class ForeverWorker
|
12
|
+
@@worked = 0
|
13
|
+
include Sidekiq::Worker
|
14
|
+
def perform()
|
15
|
+
@@worked += 1
|
16
|
+
puts "working forever!"
|
17
|
+
while true
|
18
|
+
sleep 1
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.times_worked
|
23
|
+
@@worked
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class HardWorker
|
28
|
+
@@worked = 0
|
29
|
+
@@foo = []
|
30
|
+
include Sidekiq::Worker
|
31
|
+
def perform(msg)
|
32
|
+
@@foo << "hello world" * (4**10)
|
33
|
+
puts "perform.."
|
34
|
+
@@worked += 1
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.times_worked
|
38
|
+
@@worked
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
|
2
|
+
require "helper"
|
3
|
+
|
4
|
+
require 'celluloid'
|
5
|
+
require 'sidekiq'
|
6
|
+
require 'sidekiq'
|
7
|
+
require 'sidekiq/cli'
|
8
|
+
require 'sidekiq/launcher'
|
9
|
+
require 'support/sidekiq_mock_fetcher'
|
10
|
+
require 'support/workers'
|
11
|
+
|
12
|
+
require 'micron/test_case/redir_logging'
|
13
|
+
|
14
|
+
class TestRecycler < Micron::TestCase
|
15
|
+
|
16
|
+
include Micron::TestCase::RedirLogging
|
17
|
+
@@redir_logger = Logging.logger.root
|
18
|
+
|
19
|
+
def setup
|
20
|
+
end
|
21
|
+
|
22
|
+
def teardown
|
23
|
+
@launcher.stop
|
24
|
+
Celluloid.shutdown
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_recycler
|
28
|
+
|
29
|
+
opts = {
|
30
|
+
:timeout => 0,
|
31
|
+
}
|
32
|
+
@launcher = Sidekiq::Launcher.new(opts)
|
33
|
+
@launcher.run
|
34
|
+
|
35
|
+
# do something..
|
36
|
+
MockFetcher.instance.writer.puts "forever"
|
37
|
+
|
38
|
+
sleep 1
|
39
|
+
assert_equal 1, ForeverWorker.times_worked
|
40
|
+
|
41
|
+
2.times do
|
42
|
+
MockFetcher.instance.writer.puts "hard"
|
43
|
+
end
|
44
|
+
sleep 1
|
45
|
+
assert_equal 2, HardWorker.times_worked
|
46
|
+
|
47
|
+
5.times do
|
48
|
+
MockFetcher.instance.writer.puts "hard"
|
49
|
+
end
|
50
|
+
|
51
|
+
while HardWorker.times_worked < 7 do
|
52
|
+
sleep 0.1
|
53
|
+
Thread.pass
|
54
|
+
end
|
55
|
+
|
56
|
+
puts "over limit?!"
|
57
|
+
sleep 1
|
58
|
+
assert_equal 0, @launcher.manager.ready.size, "waiting threads should have been stopped"
|
59
|
+
assert_equal 1, @launcher.manager.busy.size, "only one thread still busy (sleeping forever)"
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,154 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sidekiq-recycler
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chetan Sarva
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: sidekiq
|
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: 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
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: jeweler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
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
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: logging
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: easycov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: micron
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Gracefully recycle sidekiq processes that use too much memory
|
112
|
+
email: chetan@pixelcop.net
|
113
|
+
executables: []
|
114
|
+
extensions: []
|
115
|
+
extra_rdoc_files:
|
116
|
+
- README.md
|
117
|
+
files:
|
118
|
+
- Gemfile
|
119
|
+
- Gemfile.lock
|
120
|
+
- README.md
|
121
|
+
- Rakefile
|
122
|
+
- VERSION
|
123
|
+
- lib/sidekiq/recycler.rb
|
124
|
+
- sidekiq-recycler.gemspec
|
125
|
+
- test/helper.rb
|
126
|
+
- test/support/create_jobs.rb
|
127
|
+
- test/support/sidekiq_mock_fetcher.rb
|
128
|
+
- test/support/workers.rb
|
129
|
+
- test/test_recycler.rb
|
130
|
+
homepage: http://github.com/chetan/sidekiq-recycler
|
131
|
+
licenses:
|
132
|
+
- MIT
|
133
|
+
metadata: {}
|
134
|
+
post_install_message:
|
135
|
+
rdoc_options: []
|
136
|
+
require_paths:
|
137
|
+
- lib
|
138
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
139
|
+
requirements:
|
140
|
+
- - '>='
|
141
|
+
- !ruby/object:Gem::Version
|
142
|
+
version: '0'
|
143
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
|
+
requirements:
|
145
|
+
- - '>='
|
146
|
+
- !ruby/object:Gem::Version
|
147
|
+
version: '0'
|
148
|
+
requirements: []
|
149
|
+
rubyforge_project:
|
150
|
+
rubygems_version: 2.1.5
|
151
|
+
signing_key:
|
152
|
+
specification_version: 4
|
153
|
+
summary: Recycle large sidekiq processes
|
154
|
+
test_files: []
|