hive 0.1.8
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/lib/hive/scheduler.rb +77 -0
- data/lib/hive/worker.rb +114 -0
- metadata +63 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'facets/duration' unless 1.respond_to? :minute
|
3
|
+
|
4
|
+
require 'hive/worker'
|
5
|
+
|
6
|
+
module Hive
|
7
|
+
|
8
|
+
class Scheduler < Hive::Worker
|
9
|
+
|
10
|
+
def stop_tasks
|
11
|
+
threads = Thread.list.select { |t| t.alive? && t != Thread.current }
|
12
|
+
until threads.empty?
|
13
|
+
logger.info "#{threads.size} threads still running ... "
|
14
|
+
threads.each { |t| t.kill && t != Thread.current }
|
15
|
+
threads = threads.select { |t| t.alive? }
|
16
|
+
end
|
17
|
+
logger.info "All threads are finished."
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def schedule( method, options )
|
23
|
+
raise ArgumentError.new( "Invalid schedule for task #{method}.") unless valid?(options)
|
24
|
+
logger.info "Scheduling task #{method} ..."
|
25
|
+
Thread.new do
|
26
|
+
t = Time.now
|
27
|
+
loop do
|
28
|
+
t += 60
|
29
|
+
# the rescue handles the unusual case where t - Time.now is negative
|
30
|
+
sleep( ( options[:interval] || ( t - Time.now ) ).to_i ) rescue nil
|
31
|
+
if match( t, options )
|
32
|
+
if self.respond_to?( method )
|
33
|
+
Thread.new do
|
34
|
+
logger.info "Running task #{method} ..."
|
35
|
+
begin
|
36
|
+
self.send( method )
|
37
|
+
rescue Exception => e
|
38
|
+
logger.error e
|
39
|
+
end
|
40
|
+
logger.info "Task #{method} complete."
|
41
|
+
end
|
42
|
+
else
|
43
|
+
logger.warn "Task #{method} scheduled but not defined."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def valid?( options )
|
51
|
+
! ( options.keys & [ :minute, :hour, :day, :weekday, :month, :interval] ).empty?
|
52
|
+
end
|
53
|
+
|
54
|
+
def match( t, options )
|
55
|
+
options[:interval] ||
|
56
|
+
(( match_item( options[:minute], t.min )) &&
|
57
|
+
( match_item( options[:hour], t.hour )) &&
|
58
|
+
( match_item( options[:day], t.day )) &&
|
59
|
+
( match_item( options[:weekday], t.wday )) &&
|
60
|
+
( match_item( options[:month], t.month )))
|
61
|
+
end
|
62
|
+
|
63
|
+
def match_item( x, y )
|
64
|
+
case x
|
65
|
+
when nil then true
|
66
|
+
when Array then x.include? y
|
67
|
+
when Range then x.include? y
|
68
|
+
when Integer then x == y
|
69
|
+
else
|
70
|
+
logger.warn "Value #{x.inspect} will never be matched"
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/lib/hive/worker.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
module Kernel
|
2
|
+
def safe_trap(*signals)
|
3
|
+
signals.each { |s| trap(s) { yield } }
|
4
|
+
Thread.new { loop { sleep 1 } } if RUBY_PLATFORM =~ /mswin32/
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
module Hive
|
9
|
+
|
10
|
+
# "Workers" are just dedicated processes. Managers, Servers, and Monitors are all
|
11
|
+
# examples of Workers. This class just encapsulates the common features across all
|
12
|
+
# Workers: daemonization, signal traps, console support, logging, only-ness, etc.
|
13
|
+
|
14
|
+
class Worker
|
15
|
+
|
16
|
+
def self.run( options = {} )
|
17
|
+
@instance ||= new( options )
|
18
|
+
@instance.start
|
19
|
+
end
|
20
|
+
|
21
|
+
# make this the one-and-only
|
22
|
+
def self.instance ; @instance ; end
|
23
|
+
class << self ; private :new, :allocate ; end
|
24
|
+
private :dup, :clone
|
25
|
+
|
26
|
+
attr_accessor :logger, :options
|
27
|
+
|
28
|
+
def initialize( options )
|
29
|
+
@options = options
|
30
|
+
end
|
31
|
+
|
32
|
+
# returns the PID of the new process
|
33
|
+
def start
|
34
|
+
pid = daemonize if options[ :daemon ]
|
35
|
+
puts "#{self.class.name} process #{pid} started ..." if pid
|
36
|
+
return pid if pid
|
37
|
+
begin
|
38
|
+
# from here on in, we're in the daemon
|
39
|
+
start_logger ; logger.info "#{self.class} starting ..."
|
40
|
+
start_debugger if options[:debug] # unless Kernel.engine == 'jruby'
|
41
|
+
# various ways to talk to a worker
|
42
|
+
set_traps ; start_console ; start_drb
|
43
|
+
start_tasks.join
|
44
|
+
rescue Exception => e
|
45
|
+
logger.error e.to_s rescue nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def stop
|
50
|
+
logger.info "#{self.class} shutting down ..."
|
51
|
+
@console.stop if @console
|
52
|
+
stop_tasks
|
53
|
+
end
|
54
|
+
|
55
|
+
def restart ; stop ; start ; end
|
56
|
+
|
57
|
+
def daemonize
|
58
|
+
pwd = Dir.pwd ; pid = fork ; return pid if pid ; Dir.chdir( pwd )
|
59
|
+
# Make sure all file descriptors are closed
|
60
|
+
ObjectSpace.each_object( IO ) do | io |
|
61
|
+
unless [ STDIN, STDOUT, STDERR ].include?(io)
|
62
|
+
begin
|
63
|
+
unless io.closed?
|
64
|
+
io.close
|
65
|
+
end
|
66
|
+
rescue ::Exception
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
File.umask 0000 ; STDIN.reopen( '/dev/null') ;
|
71
|
+
STDOUT.reopen( '/dev/null', 'a' ) ; STDERR.reopen( STDOUT )
|
72
|
+
nil # return nil for child process, just like fork does
|
73
|
+
end
|
74
|
+
|
75
|
+
def set_traps
|
76
|
+
safe_trap( 'HUP' ) { restart }
|
77
|
+
safe_trap( 'TERM','INT' ) { stop }
|
78
|
+
end
|
79
|
+
|
80
|
+
def start_console
|
81
|
+
# TODO: add live console support
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_drb
|
85
|
+
# TODO: add DRb support
|
86
|
+
end
|
87
|
+
|
88
|
+
def start_debugger
|
89
|
+
require 'ruby-debug' ; Debugger.start
|
90
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
91
|
+
logger.info "ruby-debug enabled"
|
92
|
+
end
|
93
|
+
|
94
|
+
def start_logger
|
95
|
+
if options[ :logger ]
|
96
|
+
@logger = options[ :logger ]
|
97
|
+
else
|
98
|
+
require 'logger'
|
99
|
+
@logger = Logger.new( 'log.out' )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
# workers should override these methods
|
106
|
+
def start_tasks
|
107
|
+
end
|
108
|
+
|
109
|
+
def stop_tasks
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hive
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.8
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dan Yoder
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-30 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: facets
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "1.0"
|
24
|
+
version:
|
25
|
+
description:
|
26
|
+
email: dan@zeraweb.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- lib/hive/scheduler.rb
|
35
|
+
- lib/hive/worker.rb
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/dyoder/hive
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.8.6
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.3.1
|
59
|
+
signing_key:
|
60
|
+
specification_version: 2
|
61
|
+
summary: Open-source Ruby framework for background processing.
|
62
|
+
test_files: []
|
63
|
+
|