eycloud-recipe-resque 1.0
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.
- data/Gemfile +3 -0
- data/Gemfile.lock +16 -0
- data/README.md +43 -0
- data/Rakefile +1 -0
- data/attributes/default.rb +1 -0
- data/eycloud-recipe-resque.gemspec +21 -0
- data/files/default/resque_kill_stale +42 -0
- data/libraries/get_resque_worker_count.rb +18 -0
- data/metadata.json +17 -0
- data/metadata.rb +7 -0
- data/recipes/configure.rb +60 -0
- data/recipes/default.rb +8 -0
- data/recipes/restart.rb +14 -0
- data/templates/default/resque.rb.erb +73 -0
- data/templates/default/resque.yml.erb +1 -0
- data/templates/default/resque_scheduler.rb.erb +71 -0
- metadata +78 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README.md
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Resque recipe for EY Cloud
|
2
|
+
|
3
|
+
Resque is a Redis-backed Ruby library for creating background jobs, placing those jobs on multiple queues, and processing them later.
|
4
|
+
|
5
|
+
This recipe will setup `resque` on a Solo instance environment or on named Utility instances in a cluster environment.
|
6
|
+
|
7
|
+
Name your Utility instances with prefixes: `resque`. For example, `resque`, or `resque123`. Naming them all `resque` works just fine.
|
8
|
+
|
9
|
+
This recipe assumes that redis is running within the environment. Fortunately, it is. By default EY Cloud runs redis on your `solo` or `db_master` instance. Optionally, this recipe will use a custom `redis` utility instance that you might have in your environment. It's clever like that.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
## Simple Installation
|
14
|
+
|
15
|
+
To add this recipe to your collection of recipes, or as your first recipe, you can use the helpful `ey-recipes` command line tool:
|
16
|
+
|
17
|
+
cd myapp
|
18
|
+
gem install engineyard engineyard-recipes
|
19
|
+
ey-recipes init
|
20
|
+
ey-recipes clone git://github.com/engineyard/eycloud-recipe-resque.git -n resque
|
21
|
+
ey recipes upload --apply
|
22
|
+
|
23
|
+
If you want to have your recipes run during deploy (rather than the separate `ey recipes upload --apply` step):
|
24
|
+
|
25
|
+
ey-recipes init -d
|
26
|
+
ey-recipes clone git://github.com/engineyard/eycloud-recipe-resque.git -n resque
|
27
|
+
git add .; git commit -m "added delayed job recipe"; git push origin master
|
28
|
+
ey deploy
|
29
|
+
|
30
|
+
## Manual Installation
|
31
|
+
|
32
|
+
Clone/copy this repository into a `cookbooks/resque` folder (such that you have a `cookbooks/resque/recipes/default.rb` file). This recipe must be installed as `resque` and not `eycloud-recipe-resque` or anything fancy.
|
33
|
+
|
34
|
+
Then add the following to `cookbooks/main/recipes/default.rb`:
|
35
|
+
|
36
|
+
require_recipe "resque"
|
37
|
+
|
38
|
+
Make sure this and any customizations to the recipe are committed to your own fork of this
|
39
|
+
repository.
|
40
|
+
|
41
|
+
Then to upload and apply to EY Cloud for a given environment:
|
42
|
+
|
43
|
+
ey recipes upload --apply -e target-environment
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1 @@
|
|
1
|
+
worker_memory(300) # Mb
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
version = "1.0" # get from metadata.json or .rb
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "eycloud-recipe-resque"
|
8
|
+
s.version = version
|
9
|
+
s.authors = ["Dr Nic Williams"]
|
10
|
+
s.email = ["drnicwilliams@gmail.com"]
|
11
|
+
s.homepage = ""
|
12
|
+
s.summary = %q{Resque for EY Cloud} # from metadata
|
13
|
+
s.description = %q{Resque for EY Cloud} # from metadata long_description
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency("rake")
|
21
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "syslog"
|
4
|
+
require "fileutils"
|
5
|
+
|
6
|
+
GRACE = 300
|
7
|
+
|
8
|
+
unless ARGV.size == 1
|
9
|
+
$stderr.puts "Usage: #{$0} ttl_files_directory"
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
ttl_files_directory = ARGV.shift
|
14
|
+
|
15
|
+
unless File.directory?(ttl_files_directory)
|
16
|
+
$stderr.puts "ttl_files_directory `#{ttl_files_directory}` is not a directory"
|
17
|
+
exit 1
|
18
|
+
end
|
19
|
+
|
20
|
+
Syslog.open
|
21
|
+
|
22
|
+
Dir[File.join(ttl_files_directory, "*.ttl")].each do |file|
|
23
|
+
begin
|
24
|
+
pid = File.basename(file, ".ttl").to_i
|
25
|
+
if pid == 0
|
26
|
+
FileUtils.rm_f(file)
|
27
|
+
next
|
28
|
+
end
|
29
|
+
|
30
|
+
started = File.stat(file).mtime
|
31
|
+
elapsed = (Time.now - started).to_i
|
32
|
+
queue, ttl = File.readlines(file).map {|l| l.chomp }
|
33
|
+
ttl = ttl.to_i + GRACE
|
34
|
+
|
35
|
+
if elapsed > ttl
|
36
|
+
Syslog.notice("killing child #{pid} with HUP. processing #{queue.inspect}, elapsed #{elapsed} > ttl #{ttl}")
|
37
|
+
Process.kill("HUP", pid)
|
38
|
+
end
|
39
|
+
rescue Errno::ENOENT, Errno::ESRCH
|
40
|
+
FileUtils.rm_f(file)
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Chef
|
2
|
+
class Recipe
|
3
|
+
def get_resque_worker_count
|
4
|
+
# $ sudo cat /proc/meminfo
|
5
|
+
# MemTotal: 1759228 kB
|
6
|
+
# ...
|
7
|
+
mem_total_kb = `awk '/MemTotal/{print $2}' /proc/meminfo`.strip.to_i
|
8
|
+
mem_total_mb = mem_total_kb / 1024
|
9
|
+
|
10
|
+
# Want no more than ((total memory - 300) / est process size) workers + jobs to run
|
11
|
+
# in other words, if all workers on the instance are running jobs, there should
|
12
|
+
# be memory left over
|
13
|
+
result = (mem_total_mb - node[:worker_memory].to_i) / node[:worker_memory].to_i
|
14
|
+
result /= 2 if node[:instance_role] == 'solo' || node[:instance_role] == 'eylocal'
|
15
|
+
result
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/metadata.json
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
{
|
2
|
+
"license": "MIT",
|
3
|
+
"maintainer": "Engine Yard LLC",
|
4
|
+
"maintainer_email": "drnic@engineyard.com",
|
5
|
+
"version": "1.0",
|
6
|
+
"description": "Resque for EY Cloud",
|
7
|
+
"name": "resque",
|
8
|
+
"attributes": {
|
9
|
+
|
10
|
+
},
|
11
|
+
"long_description": "",
|
12
|
+
"dependencies": {
|
13
|
+
"cron": [
|
14
|
+
|
15
|
+
]
|
16
|
+
}
|
17
|
+
}
|
data/metadata.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#
|
2
|
+
# Cookbook Name:: resque
|
3
|
+
# Recipe:: configure
|
4
|
+
#
|
5
|
+
|
6
|
+
if node[:instance_role] == "solo" || node[:instance_role] == "eylocal" ||
|
7
|
+
(node[:instance_role] == "util" && node[:name] =~ /^(resque)/)
|
8
|
+
resque_workers_count = get_resque_worker_count()
|
9
|
+
|
10
|
+
remote_file "/usr/local/bin/resque_kill_stale" do
|
11
|
+
owner 'root'
|
12
|
+
group 'root'
|
13
|
+
mode 0755
|
14
|
+
source "resque_kill_stale"
|
15
|
+
end
|
16
|
+
|
17
|
+
directory "/tmp/resque_ttls" do
|
18
|
+
owner "root"
|
19
|
+
group "root"
|
20
|
+
mode 0755
|
21
|
+
end
|
22
|
+
|
23
|
+
# crond "Kill stale resque workers" do
|
24
|
+
# filename "resque_kill_stale"
|
25
|
+
# interval "* * * * *"
|
26
|
+
# command %Q{/usr/local/bin/resque_kill_stale /tmp/resque_ttls}
|
27
|
+
# end
|
28
|
+
|
29
|
+
directory "/var/log/resque" do
|
30
|
+
owner node[:owner_name]
|
31
|
+
group node[:owner_name]
|
32
|
+
mode 0755
|
33
|
+
end
|
34
|
+
|
35
|
+
node[:applications].each do |app_name, data|
|
36
|
+
# app-server specific recipes usually take care of this
|
37
|
+
link "/data/#{app_name}/shared/log" do
|
38
|
+
to "/var/log/resque"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if %w[solo app app_master util].include? node[:instance_role]
|
44
|
+
redis_instance = node.engineyard.environment.utility_instances.find {|x| x.name == "redis"} ||
|
45
|
+
node.engineyard.environment.db_master ||
|
46
|
+
node.engineyard.environment.instances.find { |x| x.role == "solo" }
|
47
|
+
|
48
|
+
node[:applications].each do |app, data|
|
49
|
+
template "/data/#{app}/shared/config/resque.yml" do
|
50
|
+
owner node[:owner_name]
|
51
|
+
group node[:owner_name]
|
52
|
+
mode 0655
|
53
|
+
source "resque.yml.erb"
|
54
|
+
variables(:framework_env => node[:environment][:framework_env],
|
55
|
+
:redis_host => redis_instance.public_hostname,
|
56
|
+
:redis_port => 6379)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/recipes/default.rb
ADDED
data/recipes/restart.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#
|
2
|
+
# Cookbook Name:: resque
|
3
|
+
# Recipe:: default
|
4
|
+
#
|
5
|
+
|
6
|
+
node[:applications].each do |app_name, data|
|
7
|
+
execute "ensure-resque-is-setup-with-monit" do
|
8
|
+
command %Q{monit reload}
|
9
|
+
end
|
10
|
+
|
11
|
+
execute "restart-resque" do
|
12
|
+
command %Q{echo "sleep 20 && monit -g #{app_name}_resque restart all" | at now }
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
rack_env = "<%= @node[:environment][:framework_env] %>"
|
2
|
+
app_root = "/data/awsm/current"
|
3
|
+
owner = "<%= @node[:owner_name] %>"
|
4
|
+
home = "/home/#{owner}"
|
5
|
+
instance_id = "<%= @node.engineyard.id %>"
|
6
|
+
|
7
|
+
<%= @resque_workers_count %>.times do |num|
|
8
|
+
inline = "#{home}/.ruby_inline/resque-#{num}"
|
9
|
+
|
10
|
+
God.watch do |w|
|
11
|
+
w.name = "resque-#{num}"
|
12
|
+
w.group = 'resque'
|
13
|
+
w.uid = owner
|
14
|
+
w.gid = owner
|
15
|
+
w.interval = 30.seconds
|
16
|
+
w.log = "#{app_root}/log/worker.#{num}.log"
|
17
|
+
w.dir = app_root
|
18
|
+
w.env = {
|
19
|
+
"VERBOSE" => "true",
|
20
|
+
"INSTANCE_ID" => instance_id,
|
21
|
+
"GOD_WATCH" => w.name,
|
22
|
+
"RACK_ENV" => rack_env,
|
23
|
+
"LANG" => "en_US.utf-8",
|
24
|
+
"MERB_ENV" => rack_env,
|
25
|
+
"HOME" => home,
|
26
|
+
"INLINEDIR" => inline
|
27
|
+
}
|
28
|
+
|
29
|
+
w.start = "bundle exec rake --trace resque:work"
|
30
|
+
|
31
|
+
w.behavior(:clean_pid_file)
|
32
|
+
|
33
|
+
w.start_grace = 2.minutes
|
34
|
+
w.restart_grace = 2.minutes
|
35
|
+
|
36
|
+
# retart if memory gets too high
|
37
|
+
w.transition(:up, :restart) do |on|
|
38
|
+
on.condition(:memory_usage) do |c|
|
39
|
+
c.above = 350.megabytes
|
40
|
+
c.times = 2
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# determine the state on startup
|
45
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
46
|
+
on.condition(:process_running) do |c|
|
47
|
+
c.running = true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# determine when process has finished starting
|
52
|
+
w.transition([:start, :restart], :up) do |on|
|
53
|
+
on.condition(:process_running) do |c|
|
54
|
+
c.running = true
|
55
|
+
c.interval = 5.seconds
|
56
|
+
end
|
57
|
+
|
58
|
+
# failsafe
|
59
|
+
on.condition(:tries) do |c|
|
60
|
+
c.times = 5
|
61
|
+
c.transition = :start
|
62
|
+
c.interval = 5.seconds
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# start if process is not running
|
67
|
+
w.transition(:up, :start) do |on|
|
68
|
+
on.condition(:process_running) do |c|
|
69
|
+
c.running = false
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= @framework_env %>: <%= @redis_host %>:<%= @redis_port %>
|
@@ -0,0 +1,71 @@
|
|
1
|
+
rack_env = "<%= @node[:environment][:framework_env] %>"
|
2
|
+
app_root = "/data/awsm/current"
|
3
|
+
owner = "<%= @node[:owner_name] %>"
|
4
|
+
home = "/home/#{owner}"
|
5
|
+
instance_id = "<%= @node.engineyard.id %>"
|
6
|
+
|
7
|
+
inline = "#{home}/.ruby_inline/resque-scheduler"
|
8
|
+
|
9
|
+
God.watch do |w|
|
10
|
+
w.name = "resque-scheduler"
|
11
|
+
w.group = 'resque'
|
12
|
+
w.uid = owner
|
13
|
+
w.gid = owner
|
14
|
+
w.interval = 30.seconds
|
15
|
+
w.log = "#{app_root}/log/resque_scheduler.log"
|
16
|
+
w.dir = app_root
|
17
|
+
w.env = {
|
18
|
+
"VERBOSE" => "true",
|
19
|
+
"INSTANCE_ID" => instance_id,
|
20
|
+
"GOD_WATCH" => w.name,
|
21
|
+
"RACK_ENV" => rack_env,
|
22
|
+
"MERB_ENV" => rack_env,
|
23
|
+
"LANG" => "en_US.utf-8",
|
24
|
+
"HOME" => home,
|
25
|
+
"INLINEDIR" => inline
|
26
|
+
}
|
27
|
+
|
28
|
+
w.start = "bundle exec rake --trace resque:scheduler"
|
29
|
+
|
30
|
+
w.behavior(:clean_pid_file)
|
31
|
+
|
32
|
+
w.start_grace = 2.minutes
|
33
|
+
w.restart_grace = 2.minutes
|
34
|
+
|
35
|
+
# retart if memory gets too high
|
36
|
+
w.transition(:up, :restart) do |on|
|
37
|
+
on.condition(:memory_usage) do |c|
|
38
|
+
c.above = 350.megabytes
|
39
|
+
c.times = 2
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# determine the state on startup
|
44
|
+
w.transition(:init, { true => :up, false => :start }) do |on|
|
45
|
+
on.condition(:process_running) do |c|
|
46
|
+
c.running = true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# determine when process has finished starting
|
51
|
+
w.transition([:start, :restart], :up) do |on|
|
52
|
+
on.condition(:process_running) do |c|
|
53
|
+
c.running = true
|
54
|
+
c.interval = 5.seconds
|
55
|
+
end
|
56
|
+
|
57
|
+
# failsafe
|
58
|
+
on.condition(:tries) do |c|
|
59
|
+
c.times = 5
|
60
|
+
c.transition = :start
|
61
|
+
c.interval = 5.seconds
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# start if process is not running
|
66
|
+
w.transition(:up, :start) do |on|
|
67
|
+
on.condition(:process_running) do |c|
|
68
|
+
c.running = false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eycloud-recipe-resque
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '1.0'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Dr Nic Williams
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-03-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: &70298707999080 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70298707999080
|
25
|
+
description: Resque for EY Cloud
|
26
|
+
email:
|
27
|
+
- drnicwilliams@gmail.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- Gemfile
|
33
|
+
- Gemfile.lock
|
34
|
+
- README.md
|
35
|
+
- Rakefile
|
36
|
+
- attributes/default.rb
|
37
|
+
- eycloud-recipe-resque.gemspec
|
38
|
+
- files/default/resque_kill_stale
|
39
|
+
- libraries/get_resque_worker_count.rb
|
40
|
+
- metadata.json
|
41
|
+
- metadata.rb
|
42
|
+
- recipes/configure.rb
|
43
|
+
- recipes/default.rb
|
44
|
+
- recipes/restart.rb
|
45
|
+
- templates/default/resque.rb.erb
|
46
|
+
- templates/default/resque.yml.erb
|
47
|
+
- templates/default/resque_scheduler.rb.erb
|
48
|
+
homepage: ''
|
49
|
+
licenses: []
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ! '>='
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '0'
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
hash: 2470771934776039979
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
hash: 2470771934776039979
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.8.17
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Resque for EY Cloud
|
78
|
+
test_files: []
|