Class: Workhorse::Daemon

Inherits:
Object
  • Object
show all
Defined in:
lib/workhorse/daemon.rb

Defined Under Namespace

Classes: ShellHandler, Worker

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(pidfile: nil, quiet: false) {|ScopedEnv.new(self, [:worker])| ... } ⇒ Daemon

Returns a new instance of Daemon.

Yields:



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/workhorse/daemon.rb', line 19

def initialize(pidfile: nil, quiet: false, &_block)
  @pidfile = pidfile
  @quiet = quiet
  @workers = []

  yield ScopedEnv.new(self, [:worker])

  @count = @workers.count

  fail 'No workers are defined.' if @count < 1

  FileUtils.mkdir_p('tmp/pids')

  if @pidfile.nil?
    @pidfile = @count > 1 ? 'tmp/pids/workhorse.%i.pid' : 'tmp/pids/workhorse.pid'
  elsif @count > 1 && !@pidfile.include?('%s')
    fail 'Pidfile must include placeholder "%s" for worker id when specifying a count > 1.'
  elsif @count == 0 && @pidfile.include?('%s')
    fail 'Pidfile must not include placeholder "%s" for worker id when specifying a count of 1.'
  end
end

Instance Attribute Details

#workersObject (readonly)



17
18
19
# File 'lib/workhorse/daemon.rb', line 17

def workers
  @workers
end

Instance Method Details

#restartObject



139
140
141
142
# File 'lib/workhorse/daemon.rb', line 139

def restart
  stop
  return start
end

#restart_loggingObject



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/workhorse/daemon.rb', line 144

def restart_logging
  code = 0

  for_each_worker do |worker|
    _pid_file, pid, active = read_pid(worker)

    next unless pid && active

    begin
      Process.kill 'HUP', pid
      puts "Worker (#{worker.name}) ##{worker.id}: Sent signal for restart-logging"
    rescue Errno::ESRCH
      warn "Worker (#{worker.name}) ##{worker.id}: Could not send signal for restart-logging, process not found"
      code = 2
    end
  end

  return code
end

#start(quiet: false) ⇒ Object



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
# File 'lib/workhorse/daemon.rb', line 45

def start(quiet: false)
  code = 0

  # Holds messages in format [[<message>, <severity>]]
  messages = []

  for_each_worker do |worker|
    pid_file, pid, active = read_pid(worker)

    if pid_file && pid && active
      messages << ["Worker ##{worker.id} (#{worker.name}): Already started (PID #{pid})", 2] unless quiet
      code = 2
    elsif pid_file
      File.delete pid_file

      shutdown_file = pid ? Workhorse::Worker.shutdown_file_for(pid) : nil
      shutdown_file = nil if shutdown_file && !File.exist?(shutdown_file)

      messages << ["Worker ##{worker.id} (#{worker.name}): Starting (stale pid file)", 1] unless quiet || shutdown_file
      start_worker worker
      FileUtils.rm(shutdown_file) if shutdown_file
    else
      messages << ["Worker ##{worker.id} (#{worker.name}): Starting", 1] unless quiet
      start_worker worker
    end
  end

  if messages.any?
    min = messages.min_by(&:last)[1]

    # Only print messages if there is at least one message with severity 1
    if min == 1
      messages.each { |(message, _severity)| warn message }
    end
  end

  return code
end

#status(quiet: false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/workhorse/daemon.rb', line 105

def status(quiet: false)
  code = 0

  for_each_worker do |worker|
    pid_file, pid, active = read_pid(worker)

    if pid_file && pid && active
      puts "Worker ##{worker.id} (#{worker.name}): Running" unless quiet
    elsif pid_file
      warn "Worker ##{worker.id} (#{worker.name}): Not running (stale PID file)" unless quiet
      code = 2
    else
      warn "Worker ##{worker.id} (#{worker.name}): Not running" unless quiet
      code = 2
    end
  end

  return code
end

#stop(kill = false, quiet: false) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/workhorse/daemon.rb', line 84

def stop(kill = false, quiet: false)
  code = 0

  for_each_worker do |worker|
    pid_file, pid, active = read_pid(worker)

    if pid_file && pid && active
      puts "Worker (#{worker.name}) ##{worker.id}: Stopping" unless quiet
      stop_worker pid_file, pid, kill: kill
    elsif pid_file
      File.delete pid_file
      puts "Worker (#{worker.name}) ##{worker.id}: Already stopped (stale PID file)" unless quiet
    else
      warn "Worker (#{worker.name}) ##{worker.id}: Already stopped" unless quiet
      code = 2
    end
  end

  return code
end

#watchObject



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/workhorse/daemon.rb', line 125

def watch
  if defined?(Rails)
    should_be_running = !File.exist?(Rails.root.join('tmp/stop.txt'))
  else
    should_be_running = true
  end

  if should_be_running && status(quiet: true) != 0
    return start(quiet: Workhorse.silence_watcher)
  else
    return 0
  end
end

#worker(name = 'Job Worker', &block) ⇒ Object



41
42
43
# File 'lib/workhorse/daemon.rb', line 41

def worker(name = 'Job Worker', &block)
  @workers << Worker.new(@workers.size + 1, name, &block)
end