Parker Smith Software

 

background process manager script

By Brian Webb

28 May 2009

We're working on a project right now that uses starling + workling for sending email in the background as well as daemons to send out send out periodic emails (also in the background). So in our development environment it quickly became a pain to start starling, then start the daemon every time someone started working on the project, then killing processes when you're done. We kept hitting errors in the code, only to realize that it was because the background task mangers weren't running. So we came up with this little script to make it easy, just:

script/background start development

and then when you're all done.

script/background stop

Here's the code, just put it in script/background of your rails project:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205




require 'optparse' 
require 'rdoc/usage'
require 'ostruct'
require 'date'


class App
  VERSION = '0.0.1'
  
  attr_reader :options

  def initialize(arguments, stdin)
    @arguments = arguments
    @stdin = stdin
    
    # Set defaults
    @options = OpenStruct.new
    @options.verbose = false
    @options.quiet = false
    @local_exists = false
    @remote_exists = false

  end #end initialize


  # Parse options, check arguments, then process the command
  def run
        
    if parsed_options? && arguments_valid? 
      
      puts "Start at #{DateTime.now}\n\n" if @options.verbose
      
      output_options if @options.verbose # [Optional]
            
      process_arguments            
      process_command
      
      puts "\nFinished at #{DateTime.now}" if @options.verbose
      
    else
      output_usage
    end
      
  end
  
  protected
  
    def parsed_options?
      
      # Specify options
      opts = OptionParser.new 
      opts.on('-v', '--version')    { output_version ; exit 0 }
      opts.on('-h', '--help')       { output_help }
      opts.on('-V', '--verbose')    { @options.verbose = true }  
      opts.on('-q', '--quiet')      { @options.quiet = true }
            
      opts.parse!(@arguments) rescue return false
      
      process_options
      true      
    end

    # Performs post-parse processing on options
    def process_options
      @options.verbose = false if @options.quiet
    end
    
    def output_options
      puts "Options:\n"
      
      @options.marshal_dump.each do |name, val|        
        puts "  #{name} = #{val}"
      end
    end

    # True if required arguments were provided
    def arguments_valid?
    
      if @arguments.length >= 1
        if @arguments[0] == "stop"
          true
        else
          if @arguments[1] == "development" || @arguments[1] == "test" || @arguments[1] == "production"
            true
          end
        end
      end
    
    end
    
    # Setup the arguments
    def process_arguments
      @start = @arguments[0] == "start" ? true : false
      @stop = @arguments[0] == "stop" ? true : false
      @env = @arguments.length > 1 ? @arguments[1] : nil
    end
    
    def output_help
      output_version
      RDoc::usage() #exits app
    end
    
    def output_usage
      RDoc::usage('usage') # gets usage from comments above
    end
    
    def output_version
      puts "#{File.basename(__FILE__)} version #{VERSION}"
    end
    
    def process_command

      if @start
        
        starling_command = <<-end_command
          cd #{File.dirname(__FILE__) + '/../'} && starling -d -P tmp/pids/starling.pid -q log/
        end_command

        workling_command = <<-end_command
          cd #{File.dirname(__FILE__) + '/../'} && RAILE_ENV=#{@env} script/workling_client start
        end_command
        
        daemon_command = <<-end_command
          cd #{File.dirname(__FILE__) + '/../'} && RAILS_ENV=#{@env} lib/daemons/mailer_ctl start
        end_command

        starling_command.gsub!(/\s+/, " ")
        workling_command.gsub!(/\s+/, " ")
        daemon_command.gsub!(/\s+/, " ")

        puts "\nStarting Starling..." unless @options.quiet
        s_success = Kernel.system(starling_command)
        if !s_success && $?.exitstatus != 0
          raise "!!! Starling startup failed !!!"
        else
          puts "Starling started successfully.\n\n" unless @options.quiet
        end
        
        sleep 1
        
        puts "Starting Workling..." unless @options.quiet
        w_success = Kernel.system(workling_command)
        if !w_success && $?.exitstatus != 0
          raise "!!! Workling startup failed !!!"
        else
          puts "Workling started successfully.\n\n" unless @options.quiet
        end
        

        
      elsif @stop
        
        starling_command = <<-end_command
          kill `ps -aef | grep starling | grep -v grep | awk '{print $2}'`
        end_command

        workling_command = <<-end_command
          cd #{File.dirname(__FILE__) + '/../'} && RAILE_ENV=#{@env} script/workling_client stop
        end_command
        
        daemon_command = <<-end_command
          cd #{File.dirname(__FILE__) + '/../'} && lib/daemons/mailer_ctl stop
        end_command

        starling_command.gsub!(/\s+/, " ")
        workling_command.gsub!(/\s+/, " ")
        daemon_command.gsub!(/\s+/, " ")
        
        puts "\nStopping Workling..." unless @options.quiet
        w_success = Kernel.system(workling_command)
        if !w_success && $?.exitstatus != 0
          raise "!!! Workling shutdown failed !!!"
        else
          puts "Workling stopped successfully.\n\n" unless @options.quiet
        end
        
        sleep 1

        puts "Stopping Starling..." unless @options.quiet
        s_success = Kernel.system(starling_command)
        if !s_success && $?.exitstatus != 0
          raise "!!! Starling shutdown failed !!!"
        else
          puts "Starling stopped successfully.\n\n" unless @options.quiet
        end
        

  
      end

    end #end process_command


    
end #end App Class


# Create and run the application
app = App.new(ARGV, STDIN)
app.run

May 05, 2010 - Sina Iman
Nice solution, I'm using it for a small project I'm doing using starling and workling. I thought how you killed the starling process was clever, but I wonder what would happen if there were multiple starling process running on the same system. This is the best solution I've found for development purposes (it's a pain to start all these processes manually), but I'm curious as to what your thoughts are about using this script in production application?
May 05, 2010 - Brian Webb
We've actually switched to using delayed_job for all background processing. In production we use daemon-spawn (http://github.com/alexvollmer/daemon-spawn) to manage the background processes with this (http://gist.github.com/104314) script. You can setup an after_deploy method in your capistrano deploy.rb to make the restart happen.
 
 
 
No Spam: 5 + 1 =
 
May 28, 2009 - Brian Webb
5
 
November 02, 2008 - Brian Webb
0