Class: ProcessBot::Process::Handlers::Sidekiq

Inherits:
Object
  • Object
show all
Defined in:
lib/process_bot/process/handlers/sidekiq.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(process) ⇒ Sidekiq

Returns a new instance of Sidekiq.



4
5
6
7
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 4

def initialize(process)
  @process = process
  @options = process.options
end

Instance Attribute Details

#optionsObject (readonly)

Returns the value of attribute options.



2
3
4
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 2

def options
  @options
end

#processObject (readonly)

Returns the value of attribute process.



2
3
4
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 2

def process
  @process
end

Instance Method Details

#current_pidObject



9
10
11
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 9

def current_pid
  process.current_pid
end

#daemonizeObject



13
14
15
16
17
18
19
20
21
22
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 13

def daemonize
  logger.logs "Daemonize!"

  pid = Process.fork do
    Process.daemon
    yield
  end

  Process.detach(pid) if pid
end

#ensure_current_pid?Boolean

Returns:

  • (Boolean)


86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 86

def ensure_current_pid?
  unless current_pid
    warn "Sidekiq not running with a PID"
    return false
  end

  unless refresh_current_pid
    logger.logs "Sidekiq PID not running and no replacement found - nothing to stop"
    return false
  end

  true
end

#fetchObject



59
60
61
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 59

def fetch(*, **)
  options.fetch(*, **)
end

#graceful(stop_process_bot: true, **_args) ⇒ Object



144
145
146
147
148
149
150
151
152
153
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 144

def graceful(stop_process_bot: true, **_args)
  process.set_stopped if stop_process_bot

  return unless ensure_current_pid?

  return unless send_tstp_or_return

  logger.logs "Wait for gracefully stopped..."
  wait_for_no_jobs_and_stop_sidekiq
end

#graceful_no_wait(stop_process_bot: true, **_args) ⇒ Object



155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 155

def graceful_no_wait(stop_process_bot: true, **_args)
  process.set_stopped if stop_process_bot

  return unless ensure_current_pid?

  return unless send_tstp_or_return

  logger.logs "Dont wait for gracefully stopped - doing that in fork..."
  daemonize do
    wait_for_no_jobs_and_stop_sidekiq
    exit
  end
end

#loggerObject



63
64
65
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 63

def logger
  @logger ||= ProcessBot::Logger.new(options: options)
end

#process_running?(pid) ⇒ Boolean

Returns:

  • (Boolean)


24
25
26
27
28
29
30
31
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 24

def process_running?(pid)
  return false unless pid

  Process.getpgid(pid)
  true
rescue Errno::ESRCH
  false
end

#refresh_current_pid(only_if_present: false) ⇒ Object



49
50
51
52
53
54
55
56
57
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 49

def refresh_current_pid(only_if_present: false)
  return nil if only_if_present && !current_pid
  return current_pid if process_running?(current_pid)

  new_pid = related_sidekiq_pid
  return nil unless new_pid

  update_current_pid(new_pid)
end


33
34
35
36
37
38
39
40
41
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 33

def related_sidekiq_pid
  related_sidekiq_processes = process.runner.related_sidekiq_processes
  if related_sidekiq_processes.empty?
    logger.logs "No related Sidekiq processes found while refreshing PID"
    return nil
  end

  related_sidekiq_processes.first.pid
end

#send_tstp_or_returnObject



77
78
79
80
81
82
83
84
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 77

def send_tstp_or_return
  logger.logs "Killing process with signal TSTP for PID #{current_pid}"
  Process.kill("TSTP", current_pid)
  true
rescue Errno::ESRCH
  logger.logs "Sidekiq PID #{current_pid} disappeared before TSTP"
  false
end

#setObject



73
74
75
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 73

def set(*, **)
  options.set(*, **)
end

#set_option(key, value) ⇒ Object



67
68
69
70
71
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 67

def set_option(key, value)
  raise "Unknown option for Sidekiq handler: #{key}" unless options.key?(key)

  set(key, value)
end

#start_commandObject

rubocop:disable Metrics/AbcSize



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 120

def start_command # rubocop:disable Metrics/AbcSize
  args = []

  options.options.each do |key, value|
    next unless (match = key.to_s.match(/\Asidekiq_(.+)\Z/))

    sidekiq_key = match[1]

    if sidekiq_key == "queue"
      value.split(",").each do |queue|
        args.push "--queue #{queue}"
      end
    else
      args.push "--#{sidekiq_key} #{value}"
    end
  end

  command = "bash -c 'cd #{options.fetch(:release_path)} && exec "
  command << "#{options.fetch(:bundle_prefix)} " if options.present?(:bundle_prefix)
  command << "bundle exec sidekiq #{args.compact.join(' ')}"
  command << "'"
  command
end

#stop(**_args) ⇒ Object



169
170
171
172
173
174
175
176
177
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 169

def stop(**_args)
  refresh_current_pid(only_if_present: true)

  if current_pid
    terminate_pid(current_pid)
  else
    terminate_related_sidekiq_processes
  end
end

#terminate_pid(pid) ⇒ Object



100
101
102
103
104
105
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 100

def terminate_pid(pid)
  logger.logs "Killing process with signal TERM for PID #{pid}"
  Process.kill("TERM", pid)
rescue Errno::ESRCH
  logger.logs "Sidekiq PID #{pid} is not running - nothing to stop"
end


107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 107

def terminate_related_sidekiq_processes
  related_sidekiq_processes = process.runner.related_sidekiq_processes

  if related_sidekiq_processes.empty?
    logger.error "#{handler_name} didn't have any processes running"
    return
  end

  related_sidekiq_processes.each do |related_sidekiq_process|
    terminate_pid(related_sidekiq_process.pid)
  end
end

#update_current_pid(new_pid) ⇒ Object



43
44
45
46
47
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 43

def update_current_pid(new_pid)
  logger.logs "Refreshing Sidekiq PID from #{current_pid || 'nil'} to #{new_pid}"
  options.events.call(:on_process_started, pid: new_pid)
  new_pid
end

#wait_for_no_jobsObject

rubocop:disable Metrics/AbcSize



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
206
207
208
209
210
211
212
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 179

def wait_for_no_jobs # rubocop:disable Metrics/AbcSize
  loop do
    found_process = false

    unless refresh_current_pid
      logger.logs "Sidekiq PID not running while waiting for jobs"
      return
    end

    Knj::Unix_proc.list("grep" => current_pid) do |process|
      process_command = process.data.fetch("cmd")
      process_pid = process.data.fetch("pid").to_i
      next unless process_pid == current_pid

      found_process = true
      sidekiq_regex = /\Asidekiq (\d+).(\d+).(\d+) (#{options.possible_process_titles_joined_regex}) \[(\d+) of (\d+)(\]|) (.+?)(\]|)\Z/
      match = process_command.match(sidekiq_regex)
      raise "Couldnt match Sidekiq command: #{process_command} with Sidekiq regex: #{sidekiq_regex}" unless match

      running_jobs = match[5].to_i

      logger.logs "running_jobs: #{running_jobs}"

      return if running_jobs.zero? # rubocop:disable Lint/NonLocalExitFromIterator
    end

    unless found_process
      logger.logs "Couldn't find running process with PID #{current_pid}"
      return
    end

    sleep 1
  end
end

#wait_for_no_jobs_and_stop_sidekiqObject



214
215
216
217
218
219
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 214

def wait_for_no_jobs_and_stop_sidekiq
  logger.logs "Wait for no jobs and Stop sidekiq"
  wait_for_no_jobs
  stop
  wait_for_sidekiq_exit
end

#wait_for_sidekiq_exitObject



221
222
223
224
225
226
227
228
# File 'lib/process_bot/process/handlers/sidekiq.rb', line 221

def wait_for_sidekiq_exit
  return unless current_pid

  while process_running?(current_pid)
    logger.logs "Waiting for Sidekiq PID #{current_pid} to stop"
    sleep 1
  end
end