angael 0.0.1 → 0.0.2
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/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: []
|