semi_daemon 0.9.1
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/Rakefile +0 -0
- data/lib/semi_daemon/version.rb +12 -0
- data/lib/semi_daemon.rb +147 -0
- data/readme.markdown +49 -0
- data/vendor/god/system/process.rb +17 -0
- metadata +71 -0
data/Rakefile
ADDED
File without changes
|
data/lib/semi_daemon.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../vendor/god/system/process"
|
2
|
+
|
3
|
+
class SemiDaemon
|
4
|
+
|
5
|
+
class SimpleLogFormatter
|
6
|
+
def call(severity, time, progname, msg)
|
7
|
+
"#{time.strftime("%Y-%m-%d %H:%M:%S")}: #{msg}\n"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
def daemon_name
|
14
|
+
self.to_s.underscore
|
15
|
+
end
|
16
|
+
|
17
|
+
def log_file(log=nil)
|
18
|
+
log ? @log_file=log : @log_file
|
19
|
+
end
|
20
|
+
|
21
|
+
def pid_file(pid_file=nil)
|
22
|
+
pid_file ? @pid_file=pid_file : @pid_file
|
23
|
+
end
|
24
|
+
|
25
|
+
def interval(int=nil)
|
26
|
+
int ? @interval=int : @interval
|
27
|
+
end
|
28
|
+
|
29
|
+
def inherited(child)
|
30
|
+
child.log_file "#{child.daemon_name}.log"
|
31
|
+
child.pid_file "#{child.daemon_name}.pid"
|
32
|
+
end
|
33
|
+
|
34
|
+
def stop_file
|
35
|
+
"#{daemon_name}.stop"
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
# create instance methods for our class methods
|
41
|
+
def method_missing(m,*args)
|
42
|
+
if [:log_file, :pid_file, :interval, :daemon_name,:stop_file].include?(m)
|
43
|
+
self.class.send(m)
|
44
|
+
else
|
45
|
+
super(m,*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# logging
|
50
|
+
|
51
|
+
def logger
|
52
|
+
@logger ||= begin
|
53
|
+
l=Logger.new root("log/#{log_file}")
|
54
|
+
l.formatter = SimpleLogFormatter.new
|
55
|
+
l
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# process management
|
60
|
+
|
61
|
+
def root(path)
|
62
|
+
if defined?(Rails)
|
63
|
+
(Rails.root + path).to_s
|
64
|
+
else
|
65
|
+
dir = File.dirname(__FILE__)
|
66
|
+
path[0..0]=="/" ? path : File.join(dir, path)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def stop_file_path
|
71
|
+
root stop_file
|
72
|
+
end
|
73
|
+
|
74
|
+
def pid_file_path
|
75
|
+
root pid_file
|
76
|
+
end
|
77
|
+
|
78
|
+
def create_pid_file
|
79
|
+
File.open(pid_file_path,"w") { |f| f.write(Process.pid) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def running?
|
83
|
+
if File.exists?(pid_file_path)
|
84
|
+
pid=File.read(pid_file_path)
|
85
|
+
God::System::Process.new(pid).exists?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def work
|
90
|
+
raise "You need to define the work method in your subclass of SemiDaemon."
|
91
|
+
end
|
92
|
+
|
93
|
+
def terminate
|
94
|
+
logger.info "#{daemon_name} stopped."
|
95
|
+
Rails.logger.info "#{daemon_name} stopped." if defined?(Rails)
|
96
|
+
File.delete(pid_file_path) rescue nil
|
97
|
+
File.delete(stop_file_path) rescue nil
|
98
|
+
exit
|
99
|
+
end
|
100
|
+
|
101
|
+
def transaction(&block)
|
102
|
+
terminate if @quit
|
103
|
+
@safe_to_quit=false
|
104
|
+
r=block.call
|
105
|
+
@safe_to_quit=true
|
106
|
+
terminate if @quit
|
107
|
+
r
|
108
|
+
end
|
109
|
+
|
110
|
+
def initialize
|
111
|
+
@safe_to_quit=true
|
112
|
+
end
|
113
|
+
|
114
|
+
def wants_quit?
|
115
|
+
File.exists?(stop_file_path) or @quit
|
116
|
+
end
|
117
|
+
|
118
|
+
def run
|
119
|
+
raise "You must specify `interval 1.minute` or similar in your class to set the interval." if interval.nil?
|
120
|
+
return if running?
|
121
|
+
Signal.trap("TERM") do
|
122
|
+
@quit=true
|
123
|
+
# if it's not safe to quit then we have to trust that our
|
124
|
+
# child class will quit when it can or else we'll quit at
|
125
|
+
# the beginning of the next event loop
|
126
|
+
terminate if @safe_to_quit
|
127
|
+
end
|
128
|
+
|
129
|
+
create_pid_file
|
130
|
+
logger.info "#{daemon_name} started (PID: #{Process.pid})."
|
131
|
+
while(true)
|
132
|
+
if wants_quit?
|
133
|
+
logger.info "Being asked to stop (#{@quit ? "TERM" : "stop file"}), stopping..."
|
134
|
+
terminate
|
135
|
+
end
|
136
|
+
logger.info "Working..."
|
137
|
+
work
|
138
|
+
if defined?(ActiveRecord::Base)
|
139
|
+
ActiveRecord::Base.clear_active_connections!
|
140
|
+
end
|
141
|
+
logger.info "Sleeping for #{interval/60} minutes..."
|
142
|
+
# we do this so we can stop more quickly while sleeping
|
143
|
+
interval.to_i.times { sleep 1 unless @quit }
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
data/readme.markdown
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
SemiDaemon
|
2
|
+
==========
|
3
|
+
|
4
|
+
Why
|
5
|
+
---
|
6
|
+
|
7
|
+
Before this I've tried `backgroundDRB`, `daemon_generator` (I think that's the name), and they never really worked to my satisfaction. I did find `background_job` for scheduled background stuff and liked how simple it was to setup and get started. SemiDaemon is kind of patterned after the simplicity of setting up it's daemon process.
|
8
|
+
|
9
|
+
|
10
|
+
An example class
|
11
|
+
----------------
|
12
|
+
|
13
|
+
class CampaignSender < SemiDaemon
|
14
|
+
|
15
|
+
# defaulted based on the name of the class if you don't provide them
|
16
|
+
# log and pid will both be stored in your RAILS_ROOT/log folder
|
17
|
+
log_file "campaign_sender.log"
|
18
|
+
pid_file "campaign_sender.pid"
|
19
|
+
|
20
|
+
# you must provide an interval that your work function is called
|
21
|
+
interval 2.hours
|
22
|
+
|
23
|
+
# your work function is called every 2 hours
|
24
|
+
def work
|
25
|
+
logger.info "Running the queue..."
|
26
|
+
Campaign.run_queue!
|
27
|
+
rescue StandardError => e
|
28
|
+
logger.error "-----"
|
29
|
+
logger.error e.message
|
30
|
+
e.backtrace.each { |x| logger.error x }
|
31
|
+
logger.error "-----"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
Setting it up with crontab
|
37
|
+
--------------------------
|
38
|
+
|
39
|
+
The following cron entry will start the daemon every hour. If the process is already running them the new process will simply quit.
|
40
|
+
|
41
|
+
0 * * * * /usr/bin/ruby /u/apps/your_app/current/script/runner -e production "CampaignSender.new.run"
|
42
|
+
|
43
|
+
If the process ever dies for any reason cron will send you the stack trace via e-mail (if you have that setup right). And one hour (or less) later the process will fire itself right back up again.
|
44
|
+
|
45
|
+
|
46
|
+
Enjoy,
|
47
|
+
|
48
|
+
Josh Goebel
|
49
|
+
Dreamer3 on #rubyonrails/freenode
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semi_daemon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 57
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 9
|
9
|
+
- 1
|
10
|
+
version: 0.9.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Josh Goebel
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-01-06 00:00:00 -05:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: simple class for creating long running daemon processes easily
|
23
|
+
email: josh@autoraptor.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- readme.markdown
|
30
|
+
files:
|
31
|
+
- Rakefile
|
32
|
+
- lib/semi_daemon/version.rb
|
33
|
+
- lib/semi_daemon.rb
|
34
|
+
- vendor/god/system/process.rb
|
35
|
+
- readme.markdown
|
36
|
+
has_rdoc: true
|
37
|
+
homepage:
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options: []
|
42
|
+
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
hash: 3
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 3
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.6.2
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: simple class for creating long running daemon processes
|
70
|
+
test_files: []
|
71
|
+
|