god 0.7.22 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,23 @@
1
+ == 0.8.0 / 2009-11-30
2
+ * Minor Enhancements
3
+ * Rubygems decontamination
4
+ * Use Monitor instead of Mutex to provide ability to wait with a timeout
5
+ * Only generate log messages when they're being used
6
+ * Remove usage of Thread.critical in DriverEventQueue
7
+ * Update to work with latest bleak-house
8
+ * Cache some frequent lookups to reduce object creation
9
+ * Changing the @io.print call in SimpleLogger to not concatenate
10
+ the formatted results before printing
11
+ * Bug fixes
12
+ * Make sure we don't leak hash slots when processes die
13
+ * Make sure the driver is shutdown on Task#unregister!
14
+ * Fix memory leak when issuing "god load" successfully
15
+ * Fix defunct process
16
+
17
+ == NOTE
18
+ At this point I will stop giving credit in the history. Look at the author
19
+ and committer in the commit for that info.
20
+
1
21
  == 0.7.22 / 2009-10-29
2
22
  * Minor Enhancements
3
23
  * Save ARGV so we can get access to it later if we want [github.com/eric]
@@ -1,4 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 7
4
- :patch: 22
3
+ :minor: 8
4
+ :build:
5
+ :patch: 0
@@ -1,20 +1,20 @@
1
1
  # Generated by jeweler
2
- # DO NOT EDIT THIS FILE
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
4
  # -*- encoding: utf-8 -*-
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{god}
8
- s.version = "0.7.22"
8
+ s.version = "0.8.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tom Preston-Werner"]
12
- s.date = %q{2009-10-29}
12
+ s.date = %q{2009-11-30}
13
13
  s.default_executable = %q{god}
14
14
  s.description = %q{God is an easy to configure, easy to extend monitoring framework written in Ruby.}
15
15
  s.email = %q{tom@mojombo.com}
16
16
  s.executables = ["god"]
17
- s.extensions = ["ext/god/extconf.rb"]
17
+ s.extensions = ["ext/god/extconf.rb", "ext/god/extconf.rb"]
18
18
  s.extra_rdoc_files = [
19
19
  "README.txt"
20
20
  ]
@@ -224,3 +224,4 @@ Gem::Specification.new do |s|
224
224
  else
225
225
  end
226
226
  end
227
+
data/lib/god.rb CHANGED
@@ -1,8 +1,5 @@
1
1
  $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
2
2
 
3
- # rubygems
4
- require 'rubygems'
5
-
6
3
  # core
7
4
  require 'stringio'
8
5
  require 'fileutils'
@@ -534,6 +531,9 @@ module God
534
531
  watches = self.pending_watches.dup
535
532
  self.pending_watches.clear
536
533
  self.pending_watch_states.clear
534
+
535
+ # make sure we quit capturing when we're done
536
+ LOG.finish_capture
537
537
  rescue Exception => e
538
538
  # don't ever let running_load take down god
539
539
  errors << LOG.finish_capture
@@ -215,6 +215,7 @@ module God
215
215
  puts "killing process"
216
216
 
217
217
  ::Process.kill('KILL', pid)
218
+ ::Process.waitpid(pid)
218
219
  rescue => e
219
220
  puts e.message
220
221
  puts e.backtrace.join("\n")
@@ -10,7 +10,6 @@
10
10
  # end
11
11
 
12
12
  module XMPP4R
13
- require 'rubygems'
14
13
  require 'xmpp4r'
15
14
  include Jabber
16
15
  end
@@ -10,7 +10,6 @@
10
10
  # c.group = 'developers'
11
11
  # end
12
12
 
13
- require 'rubygems'
14
13
  require 'twitter'
15
14
 
16
15
  module God
@@ -12,26 +12,26 @@ end
12
12
  class BleakHouseDiagnostic
13
13
  LOG_FILE = File.join(File.dirname(__FILE__), *%w[.. .. logs bleak.log])
14
14
 
15
- class << self
16
- attr_accessor :logger
17
- end
18
-
19
15
  def self.install
20
- require 'bleak_house'
21
- self.logger = BleakHouse::Logger.new
22
- File.delete(LOG_FILE) rescue nil
16
+ require 'snapshot'
17
+ self.spin
23
18
  end
24
19
 
25
- def self.snapshot(name)
26
- self.logger.snapshot(LOG_FILE, name, false) if self.logger
20
+ def self.snapshot
21
+ @count ||= 0
22
+ filename = "/tmp/god-bleak-%s-%03i.dump" % [Process.pid,@count]
23
+ STDERR.puts "** BleakHouse: working..."
24
+ BleakHouse.ext_snapshot(filename, 3)
25
+ STDERR.puts "** BleakHouse: complete\n** Bleakhouse: Run 'bleak #{filename}' to analyze."
26
+ @count += 1
27
27
  end
28
28
 
29
- def self.spin(delay = 1)
29
+ def self.spin(delay = 60)
30
30
  Thread.new do
31
31
  loop do
32
- self.snapshot
33
32
  sleep(delay)
33
+ self.snapshot
34
34
  end
35
35
  end
36
36
  end
37
- end
37
+ end
@@ -1,3 +1,5 @@
1
+ require 'monitor'
2
+
1
3
  module God
2
4
  class TimedEvent
3
5
  include Comparable
@@ -52,29 +54,24 @@ module God
52
54
  @task.send(@name, *@args)
53
55
  end
54
56
  end
55
-
56
- class DriverEventQueue
57
+
58
+ class DriverEventQueue
57
59
  def initialize
58
60
  @shutdown = false
59
- @waiting = []
60
61
  @events = []
61
- @waiting.taint
62
+ @monitor = Monitor.new
63
+ @resource = @monitor.new_cond
62
64
  @events.taint
63
65
  self.taint
64
66
  end
65
67
 
66
- #
68
+ #
67
69
  # Wake any sleeping threads after setting the sentinel
68
- #
70
+ #
69
71
  def shutdown
70
72
  @shutdown = true
71
- begin
72
- Thread.critical = true
73
- @waiting.each do |t|
74
- t.run
75
- end
76
- ensure
77
- Thread.critical = false
73
+ @monitor.synchronize do
74
+ @resource.broadcast
78
75
  end
79
76
  end
80
77
 
@@ -82,47 +79,34 @@ module God
82
79
  # Sleep until the queue has something due
83
80
  #
84
81
  def pop
85
- begin
86
- while (Thread.critical = true; @events.empty? or !@events.first.due?)
87
- @waiting.push Thread.current
88
- if @events.empty?
89
- raise ThreadError, "queue empty" if @shutdown
90
- Thread.stop
91
- else
92
- Thread.critical = false
93
- delay = @events.first.at - Time.now
94
- sleep delay if delay > 0
95
- Thread.critical = true
96
- end
82
+ @monitor.synchronize do
83
+ if @events.empty?
84
+ raise ThreadError, "queue empty" if @shutdown
85
+ @resource.wait
86
+ else !@events.first.due?
87
+ delay = @events.first.at - Time.now
88
+ @resource.wait(delay) if delay > 0
97
89
  end
90
+
98
91
  @events.shift
99
- ensure
100
- Thread.critical = false
101
92
  end
102
93
  end
103
94
 
104
95
  alias shift pop
105
96
  alias deq pop
106
97
 
107
- #
108
- # Add an event to the queue, wake any waiters if what we added needs to
98
+ #
99
+ # Add an event to the queue, wake any waiters if what we added needs to
109
100
  # happen sooner than the next pending event
110
101
  #
111
102
  def push(event)
112
- Thread.critical = true
113
- @events << event
114
- @events.sort!
115
- begin
116
- t = @waiting.shift if @events.first == event
117
- t.wakeup if t
118
- rescue ThreadError
119
- retry
120
- ensure
121
- Thread.critical = false
122
- end
123
- begin
124
- t.run if t
125
- rescue ThreadError
103
+ @monitor.synchronize do
104
+ @events << event
105
+ @events.sort!
106
+
107
+ # If we've sorted the events and found the one we're adding is at
108
+ # the front, it will likely need to run before the next due date
109
+ @resource.signal if @events.first == event
126
110
  end
127
111
  end
128
112
 
@@ -130,7 +114,7 @@ module God
130
114
  alias enq push
131
115
 
132
116
  def empty?
133
- @que.empty?
117
+ @events.empty?
134
118
  end
135
119
 
136
120
  def clear
@@ -142,16 +126,12 @@ module God
142
126
  end
143
127
 
144
128
  alias size length
145
-
146
- def num_waiting
147
- @waiting.size
148
- end
149
129
  end
150
130
 
151
131
 
152
132
  class Driver
153
133
  attr_reader :thread
154
-
134
+
155
135
  # Instantiate a new Driver and start the scheduler loop to handle events
156
136
  # +task+ is the Task this Driver belongs to
157
137
  #
@@ -176,12 +156,26 @@ module God
176
156
  end
177
157
  end
178
158
 
159
+ # Check if we're in the driver context
160
+ #
161
+ # Returns true if in driver thread
162
+ def in_driver_context?
163
+ Thread.current == @thread
164
+ end
165
+
179
166
  # Clear all events for this Driver
180
167
  #
181
168
  # Returns nothing
182
169
  def clear_events
183
170
  @events.clear
184
171
  end
172
+
173
+ # Shutdown the DriverEventQueue threads
174
+ #
175
+ # Returns nothing
176
+ def shutdown
177
+ @events.shutdown
178
+ end
185
179
 
186
180
  # Queue an asynchronous message
187
181
  # +name+ is the Symbol name of the operation
@@ -38,16 +38,12 @@ module God
38
38
  @@handler.register_process(pid, @@actions[pid].keys)
39
39
  end
40
40
 
41
- def self.deregister(pid, event=nil)
41
+ def self.deregister(pid, event)
42
42
  if watching_pid? pid
43
43
  running = ::Process.kill(0, pid.to_i) rescue false
44
- if event.nil?
45
- @@actions.delete(pid)
46
- @@handler.register_process(pid, []) if running
47
- else
48
- @@actions[pid].delete(event)
49
- @@handler.register_process(pid, @@actions[pid].keys) if running
50
- end
44
+ @@actions[pid].delete(event)
45
+ @@handler.register_process(pid, @@actions[pid].keys) if running
46
+ @@actions.delete(pid) if @@actions[pid].empty?
51
47
  end
52
48
  end
53
49
 
@@ -92,6 +88,7 @@ module God
92
88
  end
93
89
 
94
90
  ::Process.kill('KILL', pid)
91
+ ::Process.waitpid(pid)
95
92
 
96
93
  sleep(0.1)
97
94
 
@@ -108,4 +105,4 @@ module God
108
105
  end
109
106
 
110
107
  end
111
- end
108
+ end
@@ -58,21 +58,27 @@ module God
58
58
  def log(watch, level, text)
59
59
  # initialize watch log if necessary
60
60
  self.logs[watch.name] ||= Timeline.new(God::LOG_BUFFER_SIZE_DEFAULT) if watch
61
-
61
+
62
62
  # push onto capture and timeline for the given watch
63
- @templogio.truncate(0)
64
- @templogio.rewind
65
- @templog.send(level, text % [])
66
- @mutex.synchronize do
67
- @capture.puts(@templogio.string.dup) if @capture
68
- if watch && (Time.now - @spool < 2)
69
- self.logs[watch.name] << [Time.now, @templogio.string.dup]
63
+ if @capture || (watch && (Time.now - @spool < 2))
64
+ @mutex.synchronize do
65
+ @templogio.truncate(0)
66
+ @templogio.rewind
67
+ @templog.send(level, text)
68
+
69
+ message = @templogio.string.dup
70
+
71
+ if @capture
72
+ @capture.puts(message)
73
+ else
74
+ self.logs[watch.name] << [Time.now, message]
75
+ end
70
76
  end
71
77
  end
72
-
78
+
73
79
  # send to regular logger
74
- self.send(level, text % [])
75
-
80
+ self.send(level, text)
81
+
76
82
  # send to syslog
77
83
  Syslog.send(SYSLOG_EQUIVALENTS[level], text) if Logger.syslog
78
84
  end
@@ -114,7 +120,7 @@ module God
114
120
  # Returns String
115
121
  def finish_capture
116
122
  @mutex.synchronize do
117
- cap = @capture.string
123
+ cap = @capture.string if @capture
118
124
  @capture = nil
119
125
  cap
120
126
  end
@@ -144,7 +144,7 @@ module God
144
144
  #
145
145
  # Returns Task (self)
146
146
  def move(to_state)
147
- if Thread.current != self.driver.thread
147
+ if !self.driver.in_driver_context?
148
148
  # called from outside Driver
149
149
 
150
150
  # send an async message to Driver
@@ -235,7 +235,7 @@ module God
235
235
  #
236
236
  # Returns Task (self)
237
237
  def action(a, c = nil)
238
- if Thread.current != self.driver.thread
238
+ if !self.driver.in_driver_context?
239
239
  # called from outside Driver
240
240
 
241
241
  # send an async message to Driver
@@ -299,7 +299,7 @@ module God
299
299
  end
300
300
 
301
301
  def unregister!
302
- # override if necessary
302
+ driver.shutdown
303
303
  end
304
304
 
305
305
  ###########################################################################
@@ -104,7 +104,7 @@ module God
104
104
  ###########################################################################
105
105
 
106
106
  def action(a, c = nil)
107
- if Thread.current != self.driver.thread
107
+ if !self.driver.in_driver_context?
108
108
  # called from outside Driver
109
109
 
110
110
  # send an async message to Driver
@@ -177,6 +177,7 @@ module God
177
177
 
178
178
  def unregister!
179
179
  God.registry.remove(@process)
180
+ super
180
181
  end
181
182
  end
182
183
 
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require File.join(File.dirname(__FILE__), *%w[.. lib god])
2
3
  God::EventHandler.load
3
4
 
@@ -61,20 +61,20 @@ class TestTask < Test::Unit::TestCase
61
61
 
62
62
  def test_action_should_send_string_commands_to_system
63
63
  @task.foo = 'foo'
64
- Thread.current.stubs(:==).returns(true)
64
+ @task.driver.stubs(:in_driver_context?).returns(true)
65
65
  @task.expects(:system).with('foo')
66
66
  @task.action(:foo, nil)
67
67
  end
68
68
 
69
69
  def test_action_should_call_lambda_commands
70
70
  @task.foo = lambda { }
71
- Thread.current.stubs(:==).returns(true)
71
+ @task.driver.stubs(:in_driver_context?).returns(true)
72
72
  @task.foo.expects(:call)
73
73
  @task.action(:foo, nil)
74
74
  end
75
75
 
76
76
  def test_action_should_raise_not_implemented_on_non_string_or_lambda_action
77
- Thread.current.stubs(:==).returns(true)
77
+ @task.driver.stubs(:in_driver_context?).returns(true)
78
78
  assert_raise NotImplementedError do
79
79
  @task.foo = 7
80
80
  @task.action(:foo, nil)
@@ -126,7 +126,7 @@ class TestWatch < Test::Unit::TestCase
126
126
  # move
127
127
 
128
128
  def test_move_should_not_clean_up_if_from_state_is_nil
129
- Thread.current.stubs(:==).returns(true)
129
+ @watch.driver.stubs(:in_driver_context?).returns(true)
130
130
  @watch.driver.expects(:message).never
131
131
 
132
132
  metric = nil
@@ -147,7 +147,7 @@ class TestWatch < Test::Unit::TestCase
147
147
  end
148
148
 
149
149
  def test_move_should_clean_up_from_state_if_not_nil
150
- Thread.current.stubs(:==).returns(true)
150
+ @watch.driver.stubs(:in_driver_context?).returns(true)
151
151
  @watch.driver.expects(:message).never
152
152
 
153
153
  metric = nil
@@ -170,7 +170,7 @@ class TestWatch < Test::Unit::TestCase
170
170
  end
171
171
 
172
172
  def test_move_should_call_action
173
- Thread.current.stubs(:==).returns(true)
173
+ @watch.driver.stubs(:in_driver_context?).returns(true)
174
174
  @watch.driver.expects(:message).never
175
175
 
176
176
  @watch.expects(:action).with(:start)
@@ -179,7 +179,7 @@ class TestWatch < Test::Unit::TestCase
179
179
  end
180
180
 
181
181
  def test_move_should_move_to_up_state_if_no_start_or_restart_metric
182
- Thread.current.stubs(:==).returns(true)
182
+ @watch.driver.stubs(:in_driver_context?).returns(true)
183
183
  @watch.driver.expects(:message).never
184
184
 
185
185
  [:start, :restart].each do |state|
@@ -190,7 +190,7 @@ class TestWatch < Test::Unit::TestCase
190
190
  end
191
191
 
192
192
  def test_move_should_enable_destination_metric
193
- Thread.current.stubs(:==).returns(true)
193
+ @watch.driver.stubs(:in_driver_context?).returns(true)
194
194
  @watch.driver.expects(:message).never
195
195
 
196
196
  metric = nil
@@ -213,7 +213,7 @@ class TestWatch < Test::Unit::TestCase
213
213
  # action
214
214
 
215
215
  def test_action_should_pass_start_and_stop_actions_to_call_action
216
- Thread.current.stubs(:==).returns(true)
216
+ @watch.driver.stubs(:in_driver_context?).returns(true)
217
217
  @watch.driver.expects(:message).never
218
218
 
219
219
  c = Conditions::FakePollCondition.new
@@ -224,7 +224,7 @@ class TestWatch < Test::Unit::TestCase
224
224
  end
225
225
 
226
226
  def test_action_should_do_stop_then_start_if_no_restart_command
227
- Thread.current.stubs(:==).returns(true)
227
+ @watch.driver.stubs(:in_driver_context?).returns(true)
228
228
  @watch.driver.expects(:message).never
229
229
 
230
230
  c = Conditions::FakePollCondition.new
@@ -234,7 +234,7 @@ class TestWatch < Test::Unit::TestCase
234
234
  end
235
235
 
236
236
  def test_action_should_restart_to_call_action_if_present
237
- Thread.current.stubs(:==).returns(true)
237
+ @watch.driver.stubs(:in_driver_context?).returns(true)
238
238
  @watch.driver.expects(:message).never
239
239
 
240
240
  @watch.restart = lambda { }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: god
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.22
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-29 00:00:00 -07:00
12
+ date: 2009-11-30 00:00:00 -08:00
13
13
  default_executable: god
14
14
  dependencies: []
15
15
 
@@ -19,6 +19,7 @@ executables:
19
19
  - god
20
20
  extensions:
21
21
  - ext/god/extconf.rb
22
+ - ext/god/extconf.rb
22
23
  extra_rdoc_files:
23
24
  - README.txt
24
25
  files: