angael 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +2 -0
- data/angael.gemspec +1 -1
- data/lib/angael/version.rb +1 -1
- data/lib/angael/worker.rb +27 -50
- data/spec/lib/angael/worker_spec.rb +10 -2
- metadata +3 -3
data/README.md
CHANGED
data/angael.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.email = ["paul@thoughtless.ca"]
|
11
11
|
s.homepage = "http://github.com/thoughtless/angael"
|
12
12
|
s.summary = %q{Lightweight library for running repetitive background processes.}
|
13
|
-
|
13
|
+
s.description = %q{Angael is a lightweight library for running repetitive background processes. It handles the forking and signal catching, allow you to just define what the background workers should do.}
|
14
14
|
|
15
15
|
s.rubyforge_project = "angael"
|
16
16
|
|
data/lib/angael/version.rb
CHANGED
data/lib/angael/worker.rb
CHANGED
@@ -11,54 +11,25 @@ module Angael
|
|
11
11
|
# to do wrap the child process in a block. This is useful for
|
12
12
|
# exception handling. Be sure to actually fork or you may break
|
13
13
|
# something important.
|
14
|
+
# #log - If defined, this will be called at various points of interest
|
15
|
+
# with 1 String as the argument. Log levels are not supported.
|
16
|
+
# #timeout - Number of seconds to wait for the child process to exit after
|
17
|
+
# it is sent SIGINT. If you don't define this method, it waits
|
18
|
+
# 60 seconds.
|
14
19
|
module Worker
|
15
20
|
class ChildProcessNotStoppedError < StandardError; end
|
16
21
|
|
17
22
|
attr_reader :pid
|
18
23
|
|
19
|
-
# Options:
|
20
|
-
# :batch_timeout - After this number of seconds, other workers will be able
|
21
|
-
# to work on the jobs reserved by #process_jobs.
|
22
|
-
# :batch_timeout_buffer - This is the number of seconds between when the
|
23
|
-
# worker stops processing jobs and when other workers
|
24
|
-
# can start processing the jobs that this worker had
|
25
|
-
# resered. This should be set to the maximum length
|
26
|
-
# of time a single job should take, plus the maximum
|
27
|
-
# expected discrepancy between the system clocks on
|
28
|
-
# all the worker servers.
|
29
|
-
# :logger => A logger object, which should follow the Logger class in the
|
30
|
-
# standard library. Default nil, as in no logging.
|
31
|
-
# :log_level => The log level, as defined by the Logger class in the
|
32
|
-
# standard library. One of:
|
33
|
-
# Logger::FATAL
|
34
|
-
# Logger::ERROR
|
35
|
-
# Logger::WARN
|
36
|
-
# Logger::INFO # Default
|
37
|
-
# Logger::DEBUG
|
38
|
-
def initialize(attrs={})
|
39
|
-
@timeout = attrs[:timeout] || 60 # Seconds
|
40
|
-
@batch_size = attrs[:batch_size] || 1
|
41
|
-
@batch_timeout = attrs[:batch_timeout] || @batch_size * 5 # Seconds
|
42
|
-
@batch_timeout_buffer = attrs[:batch_timeout_buffer] || 5 # Seconds
|
43
|
-
@logger = attrs[:logger]
|
44
|
-
if @logger
|
45
|
-
@log_level = attrs[:log_level] || begin
|
46
|
-
require 'logger' # Only require it if it is absolutely neccessary.
|
47
|
-
Logger::INFO
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
|
53
24
|
# Loops forever, taking jobs off the queue. SIGINT will stop it after
|
54
25
|
# allowing any jobs already taken from the queue to be processed.
|
55
26
|
def start!
|
56
27
|
trap("CHLD") do
|
57
|
-
|
28
|
+
__log("trapped SIGCHLD. Child PID #{pid}.")
|
58
29
|
|
59
30
|
# @stopping is set by #stop!. If it is true, then the child process was
|
60
31
|
# expected to die. If it is false/nil, then this is unexpected.
|
61
|
-
|
32
|
+
__log("Child process died unexpectedly") unless @stopping
|
62
33
|
# Reap the child process so that #started? will return false. But we can't
|
63
34
|
# block because this may be called for a Worker when a different Worker's
|
64
35
|
# child is the process that died.
|
@@ -66,27 +37,27 @@ module Angael
|
|
66
37
|
end
|
67
38
|
|
68
39
|
@pid = fork_child do
|
69
|
-
|
40
|
+
__log("Started")
|
70
41
|
|
71
42
|
if respond_to?(:after_fork)
|
72
|
-
|
43
|
+
__log("Running after fork callback")
|
73
44
|
after_fork
|
74
|
-
|
45
|
+
__log("Finished running after fork callback")
|
75
46
|
end
|
76
47
|
|
77
48
|
@interrupted = false
|
78
49
|
trap("INT") do
|
79
|
-
|
50
|
+
__log("SIGINT Received")
|
80
51
|
@interrupted = true
|
81
52
|
end
|
82
53
|
trap("TERM") do
|
83
|
-
|
54
|
+
__log("SIGTERM Received")
|
84
55
|
@interrupted = true
|
85
56
|
end
|
86
57
|
|
87
58
|
loop do
|
88
59
|
if @interrupted
|
89
|
-
|
60
|
+
__log("Child process exiting gracefully")
|
90
61
|
exit 0
|
91
62
|
end
|
92
63
|
work
|
@@ -96,7 +67,7 @@ module Angael
|
|
96
67
|
|
97
68
|
def stop!
|
98
69
|
unless started?
|
99
|
-
|
70
|
+
__log("Called stop for worker with PID #{pid} but it is not started")
|
100
71
|
return false
|
101
72
|
end
|
102
73
|
|
@@ -105,14 +76,14 @@ module Angael
|
|
105
76
|
@stopping = true
|
106
77
|
|
107
78
|
begin
|
108
|
-
|
109
|
-
Timeout::timeout(
|
79
|
+
__log("Sending SIGINT to child process with pid #{pid}.")
|
80
|
+
Timeout::timeout(timeout) do
|
110
81
|
Process.kill('INT', pid)
|
111
82
|
wait_for_child
|
112
83
|
end
|
113
84
|
rescue Timeout::Error
|
114
85
|
begin
|
115
|
-
|
86
|
+
__log("Child process with pid #{pid} did not stop within #{timeout} seconds of SIGINT. Sending SIGKILL to child process.")
|
116
87
|
# This only leaves 1 second for the SIGKILL to take effect. I don't
|
117
88
|
# know if that is enough time (or maybe too much time).
|
118
89
|
Timeout::timeout(1) do
|
@@ -122,7 +93,7 @@ module Angael
|
|
122
93
|
rescue Timeout::Error
|
123
94
|
if pid_running?
|
124
95
|
msg = "Unable to kill child process with PID: #{pid}"
|
125
|
-
|
96
|
+
__log(msg)
|
126
97
|
raise ChildProcessNotStoppedError, msg
|
127
98
|
end
|
128
99
|
end
|
@@ -148,8 +119,14 @@ module Angael
|
|
148
119
|
end
|
149
120
|
|
150
121
|
|
151
|
-
def
|
152
|
-
|
122
|
+
def __log(msg)
|
123
|
+
log(msg) if respond_to?(:log)
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
# In seconds
|
128
|
+
def timeout
|
129
|
+
60
|
153
130
|
end
|
154
131
|
|
155
132
|
|
@@ -166,7 +143,7 @@ module Angael
|
|
166
143
|
# Will just return if the child process is not running.
|
167
144
|
def wait_for_child(opts={})
|
168
145
|
begin
|
169
|
-
|
146
|
+
__log("Waiting for child with pid #{pid}.")
|
170
147
|
if opts[:dont_block]
|
171
148
|
# When this is called as the result of a SIGCHLD
|
172
149
|
# we need to pass in Process::WNOHANG as the 2nd argument, otherwise when
|
@@ -101,7 +101,11 @@ describe Angael::Worker do
|
|
101
101
|
end
|
102
102
|
|
103
103
|
context "child process does die within the worker's timeout" do
|
104
|
-
subject
|
104
|
+
subject do
|
105
|
+
worker = Angael::TestSupport::SampleWorker.new
|
106
|
+
worker.stub(:timeout => 2)
|
107
|
+
worker
|
108
|
+
end
|
105
109
|
before do
|
106
110
|
subject.stub(:work) { nil }
|
107
111
|
subject.start!
|
@@ -129,7 +133,11 @@ describe Angael::Worker do
|
|
129
133
|
end
|
130
134
|
|
131
135
|
context "child process does not die within the worker's timeout" do
|
132
|
-
subject
|
136
|
+
subject do
|
137
|
+
worker = Angael::TestSupport::SampleWorker.new
|
138
|
+
worker.stub(:timeout => 1)
|
139
|
+
worker
|
140
|
+
end
|
133
141
|
before do
|
134
142
|
subject.stub(:work) { sleep 1000 }
|
135
143
|
subject.start!
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: angael
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul Cortens
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-06-08 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
version: "0"
|
36
36
|
type: :development
|
37
37
|
version_requirements: *id002
|
38
|
-
description:
|
38
|
+
description: Angael is a lightweight library for running repetitive background processes. It handles the forking and signal catching, allow you to just define what the background workers should do.
|
39
39
|
email:
|
40
40
|
- paul@thoughtless.ca
|
41
41
|
executables: []
|