webapp_worker 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,6 @@
1
1
  ## Information
2
2
 
3
- Provides a way to have workers on your webapp servers, espeically useful for webapps that tend to scale up and down with X amount of servers in the load balancer. Also good way to not use another dependent resource like a job scheduler/queue. Keeps your application all packaged up nicely, no cron jobs to set, nothing else to think about setting up and nothing else to maintain.
3
+ Provides a way to have workers on your webapp servers, especially useful for webapps that tend to scale up and down with X amount of servers in the load balancer. Also good way to not use another dependent resource like a job scheduler/queue. Keeps your application all packaged up nicely, no cron jobs to set, nothing else to think about setting up and nothing else to maintain.
4
4
 
5
5
  ## Installation
6
6
 
@@ -14,8 +14,10 @@ or use it in your Gemfile
14
14
 
15
15
  waw -e development -f jobs.yml (parses data)
16
16
  waw -e development -f jobs.yml -j (parses and shows jobs)
17
- waw -e development -f jobs.yml -n 1 (parses and shows next X number of jobs that will run)
17
+ waw -e development -f jobs.yml -n 4 (parses and shows the next N run times, by job)
18
18
  waw -e development -f jobs.yml -r (parses and starts to run)
19
+ waw -e development -f jobs.yml -d (parses and turns on debugging)
20
+ waw -e development -f jobs.yml -v (parses and turns on verbose logging)
19
21
 
20
22
  ## Using in your webapp
21
23
 
@@ -45,39 +47,51 @@ You don't have to use a jobs file, you can specify the yaml or hash in the code
45
47
 
46
48
  ## Example Output of waw
47
49
 
50
+ See if Webapp Worker can parse the jobs file and show you all the jobs it can run from the file
51
+
48
52
  $ waw -e local -f config/jobs.yml -j
49
53
  Job File: config/jobs.yml
50
54
 
51
55
  Host: localhost
52
56
  Mailto:
53
57
  Environment: development
54
- Amount of Jobs: 9
55
-
56
- Command to Run: rake job:run
57
- Next Run: [2012-01-03 22:00:00 -0700]
58
- Command to Run: rake job:run
59
- Next Run: [2012-01-03 22:02:00 -0700]
60
- Command to Run: rake job:run
61
- Next Run: [2012-01-03 21:12:00 -0700]
62
- Command to Run: rake job:run
63
- Next Run: [2012-01-03 21:14:00 -0700]
64
- Command to Run: rake job:run
65
- Next Run: [2012-01-03 21:16:00 -0700]
66
- Command to Run: rake job:run
67
- Next Run: [2012-01-03 21:18:00 -0700]
68
- Command to Run: rake job:run
69
- Next Run: [2012-01-03 21:22:00 -0700]
70
- Command to Run: rake job:run
71
- Next Run: [2012-01-03 21:24:00 -0700]
72
- Command to Run: rake job:run
73
- Next Run: [2012-01-03 21:30:00 -0700]
58
+ Amount of Jobs: 3
59
+
60
+ Job: rake job:run
61
+ Job: rake job:run
62
+ Job: rake job:run
63
+
64
+ See when the jobs are supposed to run next, next two times, next three times, etc...
65
+
66
+ $ waw -e local -f config/jobs.yml -n 4
67
+ Job: rake job:run
68
+ Next Run Time(s): [2012-01-03 22:00:00 -0700, 2012-01-03 22:05:00 -0700, 2012-01-03 22:10:00 -0700, 2012-01-03 22:15:00 -0700]
69
+ Job: rake job:run
70
+ Next Run Time(s): [2012-01-03 22:00:00 -0700, 2012-01-03 22:05:00 -0700, 2012-01-03 22:10:00 -0700, 2012-01-03 22:15:00 -0700]
71
+ Job: rake job:run
72
+
73
+ $ waw -e local -f config/jobs.yml -r (optional -d and -v for debug and verbose)
74
+ Running Jobs
75
+
76
+ ## Other Options
77
+
78
+ You may want to know what version of the gem Webapp Worker is using, so just send a USR1 signal to it
79
+
80
+ $ kill -s USR1 11682
81
+ Webapp Worker Version: 0.0.3
82
+
83
+ You may need to turn on debugging for the Webapp Worker while its running, so just send a USR2 signal to it
84
+
85
+ $ kill -s USR2 11682
86
+ Changed logger level to Debug
74
87
 
75
88
  ## Roadmap
76
89
 
77
- - Process also needs to understand when to die and to start back up. (new version being used in the web app server)
90
+ - Use the mailto attribute in application to actually do something
78
91
  - Start having the webapp worker registering to a central point or do UDP mutlicasting to find each other.
79
92
  - Once self registering is enabled, webapp_workers need to communicate effectively.
80
- - Once communication is esatablished webapp_workers need to do the scheduling for themselves.
93
+ - Once communication is established webapp_workers need to do the scheduling for themselves.
94
+ - Do logging for each type of job in a jobs directory under tmp/webapp_worker, allowing for troubleshooting.
81
95
  - Spit out reports of the different jobs and how fast they run.
82
96
 
83
97
  ## Contributing
data/bin/waw CHANGED
@@ -15,7 +15,7 @@ where [options] are:
15
15
  opt :jobfile, "A YAML config file", :type => String, :short => "-f"
16
16
  opt :run, "Run the jobs", :default => false, :short => "-r"
17
17
  opt :jobs, "Show the jobs", :default => false, :short => "-j"
18
- opt :nextrun, "Find the next possible command(s) to run (i.e. 1,2...)", :type => Integer, :short => "-n"
18
+ opt :nextrun, "Find the next N run times, by job (use an integer)", :type => Integer, :short => "-n"
19
19
  opt :debug, "Local Debug", :short => "-d"
20
20
  opt :verbose, "Verbose Output", :short => "-v"
21
21
  end
@@ -30,8 +30,8 @@ a.parse_yaml(job_file)
30
30
 
31
31
  if opts[:nextrun] != nil
32
32
  a.next_command_run?(opts[:nextrun]).each do |command,time|
33
- puts "Next Command Run: #{command}"
34
- puts " Next Run: #{time}"
33
+ puts "Job: #{command}"
34
+ puts " Next Run Time(s): #{time}"
35
35
  end
36
36
  elsif opts[:run] == false && opts[:jobs] == false
37
37
  puts
@@ -49,8 +49,7 @@ elsif opts[:run] == false && opts[:jobs] == true
49
49
  puts
50
50
  a.jobs.each do |job|
51
51
  j = WebappWorker::Job.new(job)
52
- puts "Command to Run: #{j.command}"
53
- puts " Next Run: #{j.next_run?}"
52
+ puts "Job: #{j.command}"
54
53
  end
55
54
  else
56
55
  puts "Running Jobs"
@@ -2,6 +2,7 @@ require "webapp_worker/version"
2
2
 
3
3
  require "webapp_worker/application"
4
4
  require "webapp_worker/job"
5
+ require "webapp_worker/system"
5
6
 
6
7
  module WebappWorker
7
8
  end
@@ -1,20 +1,6 @@
1
1
  require 'socket'
2
2
  require 'timeout'
3
3
  require 'open4'
4
- require 'logger'
5
-
6
- module Process
7
- class << self
8
- def alive?(pid)
9
- begin
10
- Process.kill(0, pid.to_i)
11
- true
12
- rescue Errno::ESRCH
13
- false
14
- end
15
- end
16
- end
17
- end
18
4
 
19
5
  module WebappWorker
20
6
  class Application
@@ -44,6 +30,44 @@ module WebappWorker
44
30
  return Socket.gethostname.downcase
45
31
  end
46
32
 
33
+ def check_file_modification_time
34
+ mtime = File.mtime(@file)
35
+
36
+ if mtime != @file_mtime
37
+ @file_mtime = mtime
38
+ self.parse_yaml(@file)
39
+ end
40
+ end
41
+
42
+ def next_command_run?(til)
43
+ commands = {}
44
+ c = {}
45
+ next_commands = {}
46
+ new_jobs = []
47
+
48
+ (0..til).each do |i|
49
+ @jobs.each do |j|
50
+ new_jobs << j
51
+ end
52
+ end
53
+
54
+ new_jobs.flatten.each do |job|
55
+ j = WebappWorker::Job.new(job)
56
+ commands.store(j.command,j.next_runs?(til))
57
+ end
58
+
59
+ (commands.sort_by { |key,value| value }).collect { |key,value| c.store(key,value) }
60
+
61
+ counter = 0
62
+ c.each do |key,value|
63
+ next_commands.store(key,value)
64
+ counter = counter + 1
65
+ break if counter >= @jobs.length
66
+ end
67
+
68
+ return next_commands
69
+ end
70
+
47
71
  def next_command_run_time?
48
72
  commands = {}
49
73
  c = {}
@@ -79,162 +103,26 @@ module WebappWorker
79
103
  return next_commands
80
104
  end
81
105
 
82
- def check_file_modification_time
83
- mtime = File.mtime(@file)
84
-
85
- if mtime != @file_mtime
86
- @file_mtime = mtime
87
- self.parse_yaml(@file)
88
- end
89
- end
90
-
91
- def check_for_directory
92
- dir = "/tmp/webapp_worker"
93
-
94
- if Dir.exists?(dir)
95
- else
96
- Dir.mkdir(dir, 0700)
97
- end
98
- end
99
-
100
- def create_pid(logger)
101
- logger.info "Creating Pid File at /tmp/webapp_worker/waw.pid"
102
-
103
- File.open("/tmp/webapp_worker/waw.pid", 'w') { |f| f.write(Process.pid) }
104
- $0="Web App Worker - Job File: #{@file}"
105
-
106
- logger.info "Pid File created: #{Process.pid} at /tmp/webapp_worker/waw.pid"
107
- end
108
-
109
- def check_for_process(logger)
110
- file = "/tmp/webapp_worker/waw.pid"
111
-
112
- if File.exists?(file)
113
- possible_pid = ""
114
- pid_file = File.open(file, 'r').each { |f| possible_pid+= f }
115
- pid_file.close
116
-
117
- if Process.alive?(possible_pid)
118
- puts "Already found webapp_worker running, pid is: #{possible_pid}, exiting..."
119
- logger.fatal "Found webapp_worker already running with pid: #{possible_pid}, Pid File: #{file} exiting..."
120
- exit 1
121
- else
122
- logger.warn "Found pid file, but no process running, recreating pid file with my pid: #{Process.pid}"
123
- File.delete(file)
124
- self.create_pid(logger)
125
- end
126
- else
127
- self.create_pid(logger)
128
- end
129
-
130
- logger.info "Starting Webapp Worker"
131
- end
132
-
133
- def graceful_termination(logger)
134
- stop_loop = true
135
-
136
- begin
137
- puts
138
- puts "Graceful Termination started, waiting 60 seconds before KILL signal send"
139
- logger.info "Graceful Termination started, waiting 60 seconds before KILL signal send"
140
-
141
- Timeout::timeout(60) do
142
- @command_processes.each do |pid,command|
143
- logger.debug "Sending INT Signal to #{command} Process with PID: #{pid}"
144
- begin
145
- Process.kill("INT",pid.to_i)
146
- rescue => error
147
- end
148
- end
149
-
150
- @threads.each do |thread,command|
151
- thread.join
152
- end
153
- end
154
- rescue Timeout::Error
155
- puts "Graceful Termination bypassed, killing processes and threads"
156
- logger.info "Graceful Termination bypassed, killing processes and threads"
157
-
158
- @command_processes.each do |pid,command|
159
- logger.debug "Killing #{command} Process with PID: #{pid}"
160
- begin
161
- Process.kill("KILL",pid.to_i)
162
- rescue => error
163
- end
164
- end
165
-
166
- @threads.each do |thread,command|
167
- logger.debug "Killing Command Thread: #{command}"
168
- Thread.kill(thread)
169
- end
170
- end
171
-
172
- puts "Stopping Webapp Worker"
173
- logger.info "Stopping Webapp Worker"
174
- file = "/tmp/webapp_worker/waw.pid"
175
- File.delete(file)
176
- exit 0
177
- end
178
-
179
106
  def run(debug=nil,verbose=nil)
180
- self.check_for_directory
181
-
182
- logger = Logger.new("/tmp/webapp_worker/#{@environment}.log", 5, 5242880)
183
-
184
- if debug
185
- logger.level = Logger::DEBUG
186
- elsif verbose
187
- logger.level = Logger::INFO
188
- else
189
- logger.level = Logger::WARN
190
- end
191
-
192
107
  p = Process.fork do
193
- begin
194
- self.check_for_process(logger)
195
- rescue => error
196
- puts error.inspect
197
- logger.fatal error.inspect
198
- end
199
-
200
- @command_processes = {}
201
- @threads = {}
202
- stop_loop = false
203
-
204
- Signal.trap('HUP', 'IGNORE')
108
+ #Some Setup Work
109
+ $0="WebApp Worker - Job File: #{@file}"
110
+ waw_system = WebappWorker::System.new
111
+ waw_system.setup(debug,verbose)
112
+ logger = waw_system.logger
205
113
 
206
114
  %w(INT QUIT TERM TSTP).each do |sig|
207
115
  Signal.trap(sig) do
116
+ stop_loop = true
208
117
  logger.warn "Received a #{sig} signal, stopping current commands."
209
- self.graceful_termination(logger)
118
+ waw_system.graceful_termination(@threads,@command_processes)
210
119
  end
211
120
  end
212
121
 
213
- Signal.trap('USR1') do
214
- version = WebappWorker::VERSION
215
- puts
216
- puts "Webapp Worker Version: #{version}"
217
- logger.info "Received USR1 signal, sent version: #{version}"
218
- end
219
-
220
- Signal.trap('USR2') do
221
- logger.level = Logger::DEBUG
222
- puts
223
- puts "Changed logger level to Debug"
224
- logger.info "Changed logger level to Debug"
225
- end
226
-
227
- #Signal.trap('STOP') do |s|
228
- # #Stop Looping until
229
- # stop_loop = true
230
- # logger.warn "Received signal #{s}, pausing current loop."
231
- #end
232
- #
233
- #Signal.trap('CONT') do
234
- # #Start Looping again (catch throw?)
235
- # stop_loop = false
236
- # logger.warn "Received signal #{s}, starting current loop."
237
- #end
122
+ #WebApp Worker is setup now do the real work
123
+ @command_processes = {}
124
+ @threads = {}
125
+ stop_loop = false
238
126
 
239
127
  logger.debug "Going into Loop"
240
128
  until stop_loop
@@ -263,6 +151,8 @@ module WebappWorker
263
151
  pid, stdin, stdout, stderr = Open4::popen4 command
264
152
  @command_processes.store(pid,command)
265
153
 
154
+ #make logger log to a specific job file log file
155
+
266
156
  ignored, status = Process::waitpid2 pid
267
157
 
268
158
  if status.to_i == 0
@@ -299,6 +189,5 @@ module WebappWorker
299
189
 
300
190
  Process.detach(p)
301
191
  end
302
-
303
192
  end
304
193
  end
@@ -0,0 +1,205 @@
1
+ require 'drb'
2
+ require 'etc'
3
+ require 'logger'
4
+
5
+ module WebappWorker
6
+ class System
7
+
8
+ attr_accessor :user, :tmp_dir, :pid_file, :ipc_file, :logger
9
+
10
+ def initialize(user_supplied_hash={})
11
+ standard_hash = { user:"nobody", tmp_dir:"/tmp/webapp_worker/", pid_file:"/tmp/webapp_worker/waw.pid", ipc_file:"/tmp/webapp_worker/waw", logger:"" }
12
+
13
+ user_supplied_hash = {} unless user_supplied_hash
14
+ user_supplied_hash = standard_hash.merge(user_supplied_hash)
15
+
16
+ user_supplied_hash.each do |key,value|
17
+ self.instance_variable_set("@#{key}", value)
18
+ self.class.send(:define_method, key, proc{self.instance_variable_get("@#{key}")})
19
+ self.class.send(:define_method, "#{key}=", proc{|x| self.instance_variable_set("@#{key}", x)})
20
+ end
21
+ end
22
+
23
+ def self.process_alive?(pid)
24
+ begin
25
+ Process.kill(0, pid.to_i)
26
+ true
27
+ rescue Errno::ESRCH
28
+ false
29
+ end
30
+ end
31
+
32
+ def setup(debug=nil,verbose=nil)
33
+ self.check_for_directory
34
+ self.create_logger(debug,verbose)
35
+ self.check_for_process
36
+ self.start_listening
37
+
38
+ Signal.trap('HUP', 'IGNORE')
39
+
40
+ Signal.trap('USR1') do
41
+ version = WebappWorker::VERSION
42
+ puts
43
+ puts "Webapp Worker Version: #{version}"
44
+ logger.info "Received USR1 signal, sent version: #{version}"
45
+ end
46
+
47
+ Signal.trap('USR2') do
48
+ logger.level = Logger::DEBUG
49
+ puts
50
+ puts "Changed logger level to Debug"
51
+ logger.info "Changed logger level to Debug"
52
+ end
53
+ end
54
+
55
+ def check_for_directory
56
+ if Dir.exists?(@tmp_dir)
57
+ else
58
+ Dir.mkdir(@tmp_dir, 0700)
59
+ end
60
+ end
61
+
62
+ def create_logger(debug=nil,verbose=nil)
63
+ @logger = Logger.new("#{@tmp_dir}waw.log", 5, 5242880)
64
+
65
+ if debug
66
+ @logger.level = Logger::DEBUG
67
+ elsif verbose
68
+ @logger.level = Logger::INFO
69
+ else
70
+ @logger.level = Logger::WARN
71
+ end
72
+ end
73
+
74
+ def delete_files
75
+ @logger.fatal "Deleting both PID and IPC files"
76
+
77
+ begin
78
+ File.delete(@pid_file)
79
+ @logger.fatal "Deleted PID File: #{@pid_file}"
80
+ rescue => error
81
+ @logger.fatal "Error at Deleting PID File: #{@pid_file}: #{error}"
82
+ end
83
+
84
+ begin
85
+ File.delete(@ipc_file)
86
+ @logger.fatal "Deleted IPC File: #{@ipc_file}"
87
+ rescue => error
88
+ @logger.fatal "Error at Deleting IPC File: #{@ipc_file}: #{error}"
89
+ end
90
+ end
91
+
92
+ def create_pid
93
+ self.delete_files
94
+
95
+ @logger.info "Creating Pid File at #{@pid_file}"
96
+ File.open(@pid_file, 'w') { |f| f.write(Process.pid) }
97
+ @logger.info "Pid File created: #{Process.pid} at #{@pid_file}"
98
+ end
99
+
100
+ def check_for_process
101
+ if File.exists?(@pid_file)
102
+ possible_pid = ""
103
+ pf = File.open(@pid_file, 'r').each { |f| possible_pid+= f }
104
+ pf.close
105
+
106
+ if WebappWorker::System.process_alive?(possible_pid)
107
+ version = self.check_process_version
108
+ my_version = WebappWorker::VERSION
109
+
110
+ if version.to_s == my_version.to_s
111
+ puts "Already found webapp_worker running, pid is: #{possible_pid}, exiting..."
112
+ @logger.fatal "Found webapp_worker already running with pid: #{possible_pid}, Pid File: #{@pid_file} exiting..."
113
+ exit 1
114
+ else
115
+ puts "Found old version of webapp_worker #{version}, running with pid: #{possible_pid}, asking it to terminate since my version is: #{my_version}"
116
+ @logger.fatal "Found old version of webapp_worker #{version}, running with pid: #{possible_pid}, asking it to terminate since my version is: #{my_version}"
117
+
118
+ Process.kill("INT",possible_pid.to_i)
119
+
120
+ while WebappWorker::System.process_alive?(possible_pid)
121
+ sleep 120
122
+
123
+ begin
124
+ Process.kill("KILL",possible_pid.to_i)
125
+ rescue => error
126
+ end
127
+
128
+ self.create_pid
129
+ end
130
+ end
131
+ else
132
+ @logger.warn "Found pid file, but no process running"
133
+ self.create_pid
134
+ end
135
+ else
136
+ @logger.info "Did not find a pid file"
137
+ self.create_pid
138
+ end
139
+
140
+ @logger.info "Starting Webapp Worker"
141
+ end
142
+
143
+ def start_listening
144
+ @logger.info "Starting to listen on IPC: #{@ipc_file}"
145
+
146
+ DRb.start_service("drbunix:#{@ipc_file}", WebappWorker::VERSION)
147
+
148
+ @logger.info "Now listenting on IPC: #{@ipc_file}"
149
+ end
150
+
151
+ def check_process_version
152
+ @logger.info "Asking the processes version from IPC: #{@ipc_file}"
153
+
154
+ DRb.start_service
155
+ version = DRbObject.new_with("drbunix:#{@ipc_file}", nil)
156
+
157
+ @logger.info "The process' version is: #{version}"
158
+ return version
159
+ end
160
+
161
+ def graceful_termination(threads,command_processes)
162
+ begin
163
+ puts
164
+ puts "Graceful Termination started, waiting 60 seconds before KILL signal send"
165
+ @logger.info "Graceful Termination started, waiting 60 seconds before KILL signal send"
166
+
167
+ Timeout::timeout(60) do
168
+ command_processes.each do |pid,command|
169
+ @logger.debug "Sending INT Signal to #{command} Process with PID: #{pid}"
170
+
171
+ if WebappWorker::System.process_alive?(pid)
172
+ Process.kill("INT",pid.to_i)
173
+ end
174
+ end
175
+
176
+ threads.each do |thread,command|
177
+ thread.join
178
+ end
179
+ end
180
+ rescue Timeout::Error
181
+ puts "Graceful Termination bypassed, killing processes and threads"
182
+ @logger.info "Graceful Termination bypassed, killing processes and threads"
183
+
184
+ command_processes.each do |pid,command|
185
+ @logger.debug "Killing #{command} Process with PID: #{pid}"
186
+
187
+ if WebappWorker::System.process_alive?(pid)
188
+ Process.kill("KILL",pid.to_i)
189
+ end
190
+ end
191
+
192
+ threads.each do |thread,command|
193
+ @logger.debug "Killing Command Thread: #{command}"
194
+ Thread.kill(thread)
195
+ end
196
+ end
197
+
198
+ puts "Stopping Webapp Worker"
199
+ @logger.info "Stopping Webapp Worker"
200
+
201
+ self.delete_files
202
+ exit 0
203
+ end
204
+ end
205
+ end
@@ -1,3 +1,3 @@
1
1
  module WebappWorker
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: webapp_worker
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-16 00:00:00.000000000Z
12
+ date: 2012-01-18 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: trollop
16
- requirement: &83276430 !ruby/object:Gem::Requirement
16
+ requirement: &80202920 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - =
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.16.2
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *83276430
24
+ version_requirements: *80202920
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: open4
27
- requirement: &83272260 !ruby/object:Gem::Requirement
27
+ requirement: &80201930 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - =
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 1.3.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *83272260
35
+ version_requirements: *80201930
36
36
  description: Allow the webapp to handle your workers, no need to use a job scheduler
37
37
  email:
38
38
  - nickwillever@gmail.com
@@ -49,6 +49,7 @@ files:
49
49
  - lib/webapp_worker.rb
50
50
  - lib/webapp_worker/application.rb
51
51
  - lib/webapp_worker/job.rb
52
+ - lib/webapp_worker/system.rb
52
53
  - lib/webapp_worker/version.rb
53
54
  - webapp_worker.gemspec
54
55
  homepage: https://nictrix.github.com/webapp_worker