resque-cluster 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 720fd972bd513c6dc735a733251272c993aca7d4
4
+ data.tar.gz: 4baeac9c942beb915c36a997ac0845a1d3eeaec9
5
+ SHA512:
6
+ metadata.gz: 65ffb5acfd27b3b9a561e9be011af40089375136c2aef55707fc11b4c107837589c407347b917ef3c6e570abd62ea965f85191de143045d3b3f06201f3734f4e
7
+ data.tar.gz: e4a90b5e3b707ae20bb4034ae05272144ad75b1cdcede9d2e67b2b599f67066d56cb3ee27cfdb8f18341534b6c17a58edfbc37f8537d5b81a639f4c694d3f881
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,49 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+ #
31
+ #.DS_Store
32
+
33
+ # For TextMate
34
+ #*.tmproj
35
+ #tmtags
36
+
37
+ # For emacs:
38
+ #*~
39
+ #\#*
40
+ #.\#*
41
+
42
+ # For vim:
43
+ #*.swp
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,7 @@
1
+ AllCops:
2
+ Exclude:
3
+ - bin/**
4
+ - spec/**
5
+
6
+ LineLength:
7
+ Enabled: false
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gem 'resque-pool', '~> 0.5.0'
4
+ gem 'gru', '0.0.3'
5
+
6
+ group :development do
7
+ gem 'pry'
8
+ gem 'awesome_print'
9
+ gem 'rspec', '~> 3.1.0'
10
+ gem 'rdoc', '~> 3.12'
11
+ gem 'bundler', '~> 1.0'
12
+ gem 'jeweler', '~> 2.0.1'
13
+ gem 'simplecov', '>= 0'
14
+ gem 'rubocop', '~> 0.31'
15
+ gem 'mock_redis', '~> 0.15.0'
16
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,135 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.3.8)
5
+ ast (2.0.0)
6
+ astrolabe (1.3.0)
7
+ parser (>= 2.2.0.pre.3, < 3.0)
8
+ awesome_print (1.6.1)
9
+ builder (3.2.2)
10
+ coderay (1.1.0)
11
+ descendants_tracker (0.0.4)
12
+ thread_safe (~> 0.3, >= 0.3.1)
13
+ diff-lcs (1.2.5)
14
+ docile (1.1.5)
15
+ faraday (0.9.1)
16
+ multipart-post (>= 1.2, < 3)
17
+ git (1.2.9.1)
18
+ github_api (0.12.3)
19
+ addressable (~> 2.3)
20
+ descendants_tracker (~> 0.0.4)
21
+ faraday (~> 0.8, < 0.10)
22
+ hashie (>= 3.3)
23
+ multi_json (>= 1.7.5, < 2.0)
24
+ nokogiri (~> 1.6.3)
25
+ oauth2
26
+ gru (0.0.3)
27
+ redis (> 0.0)
28
+ hashie (3.4.1)
29
+ highline (1.7.2)
30
+ jeweler (2.0.1)
31
+ builder
32
+ bundler (>= 1.0)
33
+ git (>= 1.2.5)
34
+ github_api
35
+ highline (>= 1.6.15)
36
+ nokogiri (>= 1.5.10)
37
+ rake
38
+ rdoc
39
+ json (1.8.2)
40
+ jwt (1.5.0)
41
+ method_source (0.8.2)
42
+ mini_portile (0.6.2)
43
+ mock_redis (0.15.0)
44
+ mono_logger (1.1.0)
45
+ multi_json (1.11.0)
46
+ multi_xml (0.5.5)
47
+ multipart-post (2.0.0)
48
+ nokogiri (1.6.6.2)
49
+ mini_portile (~> 0.6.0)
50
+ oauth2 (1.0.0)
51
+ faraday (>= 0.8, < 0.10)
52
+ jwt (~> 1.0)
53
+ multi_json (~> 1.3)
54
+ multi_xml (~> 0.5)
55
+ rack (~> 1.2)
56
+ parser (2.2.2.2)
57
+ ast (>= 1.1, < 3.0)
58
+ powerpack (0.1.1)
59
+ pry (0.9.12.6)
60
+ coderay (~> 1.0)
61
+ method_source (~> 0.8)
62
+ slop (~> 3.4)
63
+ rack (1.6.1)
64
+ rack-protection (1.5.3)
65
+ rack
66
+ rainbow (2.0.0)
67
+ rake (10.4.2)
68
+ rdoc (3.12.2)
69
+ json (~> 1.4)
70
+ redis (3.2.1)
71
+ redis-namespace (1.5.2)
72
+ redis (~> 3.0, >= 3.0.4)
73
+ resque (1.25.2)
74
+ mono_logger (~> 1.0)
75
+ multi_json (~> 1.0)
76
+ redis-namespace (~> 1.3)
77
+ sinatra (>= 0.9.2)
78
+ vegas (~> 0.1.2)
79
+ resque-pool (0.5.0)
80
+ rake
81
+ resque (~> 1.22)
82
+ trollop (~> 2.0)
83
+ rspec (3.1.0)
84
+ rspec-core (~> 3.1.0)
85
+ rspec-expectations (~> 3.1.0)
86
+ rspec-mocks (~> 3.1.0)
87
+ rspec-core (3.1.7)
88
+ rspec-support (~> 3.1.0)
89
+ rspec-expectations (3.1.2)
90
+ diff-lcs (>= 1.2.0, < 2.0)
91
+ rspec-support (~> 3.1.0)
92
+ rspec-mocks (3.1.3)
93
+ rspec-support (~> 3.1.0)
94
+ rspec-support (3.1.2)
95
+ rubocop (0.31.0)
96
+ astrolabe (~> 1.3)
97
+ parser (>= 2.2.2.1, < 3.0)
98
+ powerpack (~> 0.1)
99
+ rainbow (>= 1.99.1, < 3.0)
100
+ ruby-progressbar (~> 1.4)
101
+ ruby-progressbar (1.7.5)
102
+ simplecov (0.10.0)
103
+ docile (~> 1.1.0)
104
+ json (~> 1.8)
105
+ simplecov-html (~> 0.10.0)
106
+ simplecov-html (0.10.0)
107
+ sinatra (1.4.6)
108
+ rack (~> 1.4)
109
+ rack-protection (~> 1.4)
110
+ tilt (>= 1.3, < 3)
111
+ slop (3.5.0)
112
+ thread_safe (0.3.5)
113
+ tilt (2.0.1)
114
+ trollop (2.1.2)
115
+ vegas (0.1.11)
116
+ rack (>= 1.0.0)
117
+
118
+ PLATFORMS
119
+ ruby
120
+
121
+ DEPENDENCIES
122
+ awesome_print
123
+ bundler (~> 1.0)
124
+ gru (= 0.0.3)
125
+ jeweler (~> 2.0.1)
126
+ mock_redis (~> 0.15.0)
127
+ pry
128
+ rdoc (~> 3.12)
129
+ resque-pool (~> 0.5.0)
130
+ rspec (~> 3.1.0)
131
+ rubocop (~> 0.31)
132
+ simplecov
133
+
134
+ BUNDLED WITH
135
+ 1.10.6
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Yasha Portnoy
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,84 @@
1
+ = resque-cluster
2
+
3
+ *resque-cluster* allows managing resque workers distributed across multiple hosts. It allows user to set the maximum number of workers to be running per host as well as the maximum number of workers running per cluster and maintains correct counts via a shared _Redis_ server.
4
+
5
+ *resque-cluster* is based on _resque-pool_ gem and will still run in a non clustered mode if, for example, you are anticipating expanding across multiple hosts at a later time.
6
+
7
+ == Installation
8
+
9
+ gem install resque-cluster
10
+ Or, more likely, add it to the Gemfile used by your resque workers
11
+ gem resque-cluster
12
+
13
+ == Usage
14
+
15
+ bundle exec resque-cluster_member <options>
16
+
17
+ == Options
18
+
19
+ -c, --config=<s> Path to the config(local/host maximums config in a cluster mode)
20
+ YAML file. Always required
21
+ -a, --appname=<s> Application name
22
+ -d, --daemon Run as a background daemon
23
+ -o, --stdout=<s> Redirect stdout to logfile
24
+ -e, --stderr=<s> Redirect stderr to logfile
25
+ -n, --nosync Don't sync logfiles on every write
26
+ -p, --pidfile=<s> PID file location
27
+ -E, --environment=<s> Set RAILS_ENV/RACK_ENV/RESQUE_ENV(in a cluster mode used for
28
+ namespacing the cluster)
29
+ -s, --spawn-delay=<i> Delay in milliseconds between spawning missing workers
30
+ -t, --term-graceful-wait On TERM signal, wait for workers to shut down gracefully
31
+ -r, --term-graceful On TERM signal, shut down workers gracefully
32
+ -m, --term-immediate On TERM signal, shut down workers immediately (default)
33
+ -i, --single-process-group Workers remain in the same process group as the master
34
+ -C, --cluster=<s> Name of the cluster this resque-pool belongs to. Required
35
+ if you want to launch in a cluster mode
36
+ -G, --global-config=<s> Path to the global config file(global/cluster maximums
37
+ config in a cluster mode)
38
+ -v, --version Print version and exit
39
+
40
+ == Local/Host Config
41
+
42
+ When run without --cluster option the host config creates precisely the number of workers specified in the config.
43
+ When run with --cluster option set host config specifies the maximum number of workers that can be spun up on the machine
44
+
45
+ Host config follows the following format:
46
+ foo: 1
47
+ bar: 9
48
+ "foo,bar,baz": 1
49
+
50
+ == Global/Cluster Config
51
+
52
+ Cluster config isn't used by a non cluster mode and the param is ignored. In a cluster mode, cluster config defines the maximum number of workers
53
+ that can run across the whole cluster. It also defines the cluster rebalancing mode. When set to true, workers will try to balance the load across all
54
+ cluster members for each worker.
55
+
56
+ Cluster config follows the following format:
57
+ global_maximums:
58
+ foo: 1
59
+ bar: 9
60
+ "foo,bar,baz": 1
61
+ rebalance_cluster: true
62
+
63
+ == Examples
64
+
65
+ When trying to launch without a cluster you could use a command like this:
66
+ bundle exec resque-cluster_member -c config.yml -E production -o /var/log/resque-cluster.stdout.log
67
+
68
+ When trying to launch with a cluster you could use a command like this:
69
+ bundle exec resque-cluster_member -c host_maximums.yml -E production -C prod-cluster -G cluster_config.yml -o /var/log/resque-cluster.stdout.log
70
+
71
+ == Contributing to resque-cluster
72
+
73
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
74
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
75
+ * Fork the project.
76
+ * Start a feature/bugfix branch.
77
+ * Commit and push until you are happy with your contribution.
78
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
79
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
80
+
81
+ == Copyright
82
+
83
+ Copyright (c) 2015 Yasha Portnoy. See LICENSE.txt for
84
+ further details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
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
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification...
17
+ # see http://guides.rubygems.org/specification-reference/ for more options
18
+ gem.name = 'resque-cluster'
19
+ gem.homepage = 'http://github.com/yportnoy/resque-cluster'
20
+ gem.license = 'MIT'
21
+ gem.summary = 'This gem sets up a resque pool cluster'
22
+ gem.description = 'This gem sets up a resque pool cluster'
23
+ gem.email = 'yportnoy@optoro.com'
24
+ gem.authors = ['Yasha Portnoy']
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc 'Code coverage detail'
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = 'true'
38
+ Rake::Task['spec'].execute
39
+ end
40
+
41
+ task default: :spec
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ''
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "resque-cluster #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- encoding: utf-8 -*-
3
+
4
+ gem 'resque-pool'
5
+
6
+ require 'resque/pool/cli'
7
+
8
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
9
+
10
+ require 'resque/pool/patches'
11
+ require 'resque/pool/cli_patches'
12
+ require 'resque/cluster'
13
+
14
+ Resque::Pool::CLI.run
@@ -0,0 +1,14 @@
1
+ require 'resque/cluster/member'
2
+
3
+ module Resque
4
+ # Distributed Pool is a clustered resque-pool
5
+ class Cluster
6
+ class << self
7
+ attr_accessor :config, :member
8
+
9
+ def init(started_pool)
10
+ @member = Member.new(started_pool)
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,116 @@
1
+ require 'socket'
2
+ require 'gru'
3
+
4
+ module Resque
5
+ class Cluster
6
+ # Member is a single member of a resque pool cluster
7
+ class Member
8
+ attr_reader :hostname, :pool, :local_config, :global_config
9
+
10
+ def initialize(started_pool)
11
+ @pool = started_pool
12
+ @local_config = parse_config(Cluster.config[:local_config_path])
13
+ @global_config = parse_config(Cluster.config[:global_config_path])
14
+ @global_config = @local_config if global_config.empty?
15
+ @worker_count_manager = initialize_gru
16
+
17
+ register
18
+ end
19
+
20
+ def perform
21
+ check_for_worker_count_adjustment
22
+ ping
23
+ end
24
+
25
+ def register
26
+ ping
27
+ end
28
+
29
+ def unregister
30
+ unping
31
+ remove_counts
32
+ unqueue_all_workers
33
+ end
34
+
35
+ def check_for_worker_count_adjustment
36
+ host_count_adjustment = @worker_count_manager.adjust_workers
37
+ adjust_worker_counts(host_count_adjustment) if host_count_adjustment
38
+ end
39
+
40
+ private
41
+
42
+ def global_prefix
43
+ "cluster:#{Cluster.config[:cluster_name]}:#{Cluster.config[:environment]}"
44
+ end
45
+
46
+ def member_prefix
47
+ "#{global_prefix}:#{hostname}"
48
+ end
49
+
50
+ def running_workers_key_name
51
+ "#{member_prefix}:running_workers"
52
+ end
53
+
54
+ def ping
55
+ Resque.redis.hset(global_prefix, hostname, Time.now.utc)
56
+ end
57
+
58
+ def unping
59
+ Resque.redis.hdel(global_prefix, hostname)
60
+ end
61
+
62
+ def initialize_gru
63
+ Gru.create(cluster_member_settings)
64
+ end
65
+
66
+ def hostname
67
+ @hostname ||= Socket.gethostname
68
+ end
69
+
70
+ def adjust_worker_counts(count_adjustments)
71
+ count_adjustments.each do |worker, count|
72
+ @pool.adjust_worker_counts(worker, count)
73
+ update_counts
74
+ end
75
+ end
76
+
77
+ def parse_config(config_path)
78
+ return {} unless config_path && File.exist?(config_path)
79
+ YAML.load(ERB.new(IO.read(config_path)).result)
80
+ end
81
+
82
+ def remove_counts
83
+ Resque.redis.del(running_workers_key_name)
84
+ end
85
+
86
+ def unqueue_all_workers
87
+ @worker_count_manager.release_workers
88
+ end
89
+
90
+ def unqueue_workers(workers)
91
+ workers = Array(workers)
92
+ workers.each do |worker|
93
+ @worker_count_manager.release_workers(worker)
94
+ end
95
+ end
96
+
97
+ def update_counts
98
+ current_workers = @pool.config
99
+ current_workers.each do |key, value|
100
+ Resque.redis.hset(running_workers_key_name, key, value)
101
+ end
102
+ end
103
+
104
+ def cluster_member_settings
105
+ {
106
+ cluster_maximums: @global_config["global_maximums"] || @global_config,
107
+ host_maximums: @local_config,
108
+ client_settings: Resque.redis.client.options,
109
+ rebalance_flag: @global_config["rebalance_cluster"] || false,
110
+ cluster_name: Cluster.config[:cluster_name],
111
+ environment_name: Cluster.config[:environment]
112
+ }
113
+ end
114
+ end
115
+ end
116
+ end