eycloud-recipe-resque 1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|