Class: Pitchfork::Configurator

Inherits:
Object
  • Object
show all
Includes:
Pitchfork
Defined in:
lib/pitchfork/configurator.rb

Overview

Implements a simple DSL for configuring a pitchfork server.

See github.com/Shopify/pitchfork/tree/master/examples/pitchfork.conf.rb and github.com/Shopify/pitchfork/tree/master/examples/pitchfork.conf.minimal.rb example configuration files.

See the docs/TUNING.md document for more information on tuning pitchfork.

Constant Summary collapse

RACKUP =

used to stash stuff for deferred processing of cli options in config.ru. Do not rely on this being around later on…

{
  :host => Pitchfork::Const::DEFAULT_HOST,
  :port => Pitchfork::Const::DEFAULT_PORT,
  :set_listener => false,
  :options => { :listeners => [] }
}
DEFAULTS =
{
  :soft_timeout => 20,
  :cleanup_timeout => 2,
  :spawn_timeout => 10,
  :timeout_signal => -> (_pid) { :KILL },
  :timeout => 22,
  :logger => default_logger,
  :worker_processes => 1,
  :before_fork => nil,
  :after_worker_fork => lambda { |server, worker|
    server.logger.info("worker=#{worker.nr} gen=#{worker.generation} pid=#{$$} spawned")
  },
  :after_mold_fork => lambda { |server, worker|
    server.logger.info("mold gen=#{worker.generation} pid=#{$$} spawned")
  },
  :before_worker_exit => nil,
  :after_worker_exit => lambda { |server, worker, status|
    m = if worker.nil?
      "repead unknown process (#{status.inspect})"
    elsif worker.mold?
      "mold pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
    else
      "worker=#{worker.nr rescue 'unknown'} pid=#{worker.pid rescue 'unknown'} gen=#{worker.generation rescue 'unknown'} reaped (#{status.inspect})"
    end
    if status.success?
      server.logger.info(m)
    else
      server.logger.error(m)
    end
  },
  :after_worker_ready => lambda { |server, worker|
    server.logger.info("worker=#{worker.nr} gen=#{worker.generation} ready")
  },
  :after_monitor_ready => lambda { |server|
    server.logger.info("Monitor pid=#{Process.pid} ready")
  },
  :after_worker_timeout => nil,
  :after_worker_hard_timeout => nil,
  :after_request_complete => nil,
  :early_hints => false,
  :refork_condition => nil,
  :check_client_connection => false,
  :rewindable_input => true,
  :client_body_buffer_size => Pitchfork::Const::MAX_BODY,
}

Constants included from Pitchfork

BootFailure, ClientShutdown, FORK_LOCK, F_SETPIPE_SZ, ForkFailure, REFORKING_AVAILABLE, VERSION

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Pitchfork

builder, clean_fork, listener_names, log_error, pipe, prevent_fork, socketpair, time_now

Constructor Details

#initialize(defaults = {}) ⇒ Configurator

:startdoc:



80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/pitchfork/configurator.rb', line 80

def initialize(defaults = {}) #:nodoc:
  self.set = Hash.new(:unset)
  @use_defaults = defaults.delete(:use_defaults)
  self.config_file = defaults.delete(:config_file)

  set.merge!(DEFAULTS) if @use_defaults
  defaults.each { |key, value| self.__send__(key, value) }
  Hash === set[:listener_opts] or
      set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} }
  Array === set[:listeners] or set[:listeners] = []
  load(false)
end

Instance Attribute Details

#after_loadObject

:stopdoc:



16
17
18
# File 'lib/pitchfork/configurator.rb', line 16

def after_load
  @after_load
end

#config_fileObject

:stopdoc:



16
17
18
# File 'lib/pitchfork/configurator.rb', line 16

def config_file
  @config_file
end

#setObject

:stopdoc:



16
17
18
# File 'lib/pitchfork/configurator.rb', line 16

def set
  @set
end

Instance Method Details

#[](key) ⇒ Object

:nodoc:



125
126
127
# File 'lib/pitchfork/configurator.rb', line 125

def [](key) # :nodoc:
  set[key]
end

#after_mold_fork(*args, &block) ⇒ Object



146
147
148
# File 'lib/pitchfork/configurator.rb', line 146

def after_mold_fork(*args, &block)
  set_hook(:after_mold_fork, block_given? ? block : args[0])
end

#after_monitor_ready(*args, &block) ⇒ Object



154
155
156
# File 'lib/pitchfork/configurator.rb', line 154

def after_monitor_ready(*args, &block)
  set_hook(:after_monitor_ready, block_given? ? block : args[0], 1)
end

#after_request_complete(*args, &block) ⇒ Object



174
175
176
# File 'lib/pitchfork/configurator.rb', line 174

def after_request_complete(*args, &block)
  set_hook(:after_request_complete, block_given? ? block : args[0], 3)
end

#after_worker_exit(*args, &block) ⇒ Object



170
171
172
# File 'lib/pitchfork/configurator.rb', line 170

def after_worker_exit(*args, &block)
  set_hook(:after_worker_exit, block_given? ? block : args[0], 3)
end

#after_worker_fork(*args, &block) ⇒ Object



142
143
144
# File 'lib/pitchfork/configurator.rb', line 142

def after_worker_fork(*args, &block)
  set_hook(:after_worker_fork, block_given? ? block : args[0])
end

#after_worker_hard_timeout(*args, &block) ⇒ Object



162
163
164
# File 'lib/pitchfork/configurator.rb', line 162

def after_worker_hard_timeout(*args, &block)
  set_hook(:after_worker_hard_timeout, block_given? ? block : args[0], 2)
end

#after_worker_ready(*args, &block) ⇒ Object



150
151
152
# File 'lib/pitchfork/configurator.rb', line 150

def after_worker_ready(*args, &block)
  set_hook(:after_worker_ready, block_given? ? block : args[0])
end

#after_worker_timeout(*args, &block) ⇒ Object



158
159
160
# File 'lib/pitchfork/configurator.rb', line 158

def after_worker_timeout(*args, &block)
  set_hook(:after_worker_timeout, block_given? ? block : args[0], 3)
end

#before_fork(*args, &block) ⇒ Object



138
139
140
# File 'lib/pitchfork/configurator.rb', line 138

def before_fork(*args, &block)
  set_hook(:before_fork, block_given? ? block : args[0], 1)
end

#before_worker_exit(*args, &block) ⇒ Object



166
167
168
# File 'lib/pitchfork/configurator.rb', line 166

def before_worker_exit(*args, &block)
  set_hook(:before_worker_exit, block_given? ? block : args[0], 2)
end

#check_client_connection(bool) ⇒ Object



248
249
250
# File 'lib/pitchfork/configurator.rb', line 248

def check_client_connection(bool)
  set_bool(:check_client_connection, bool)
end

#client_body_buffer_size(bytes) ⇒ Object



244
245
246
# File 'lib/pitchfork/configurator.rb', line 244

def client_body_buffer_size(bytes)
  set_int(:client_body_buffer_size, bytes, 0)
end

#commit!(server, options = {}) ⇒ Object

:nodoc:



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

def commit!(server, options = {}) #:nodoc:
  skip = options[:skip] || []
  if ready_pipe = RACKUP.delete(:ready_pipe)
    server.ready_pipe = ready_pipe
  end
  if set[:check_client_connection]
    set[:listeners].each do |address|
      if set[:listener_opts][address][:tcp_nopush] == true
        raise ArgumentError,
          "check_client_connection is incompatible with tcp_nopush:true"
      end
    end
  end
  set.each do |key, value|
    value == :unset and next
    skip.include?(key) and next
    server.__send__("#{key}=", value)
  end
end

#early_hints(bool) ⇒ Object



205
206
207
# File 'lib/pitchfork/configurator.rb', line 205

def early_hints(bool)
  set_bool(:early_hints, bool)
end

#expand_addr(address) ⇒ Object

expands “unix:path/to/foo” to a socket relative to the current path expands pathnames of sockets if relative to “~” or “~username” expands “*:port and ”:port“ to ”0.0.0.0:port“



271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/pitchfork/configurator.rb', line 271

def expand_addr(address) #:nodoc:
  return "0.0.0.0:#{address}" if Integer === address
  return address unless String === address

  case address
  when %r{\Aunix:(.*)\z}
    File.expand_path($1)
  when %r{\A~}
    File.expand_path(address)
  when %r{\A(?:\*:)?(\d+)\z}
    "0.0.0.0:#$1"
  when %r{\A\[([a-fA-F0-9:]+)\]\z}, %r/\A((?:\d+\.){3}\d+)\z/
    canonicalize_tcp($1, 80)
  when %r{\A\[([a-fA-F0-9:]+)\]:(\d+)\z}, %r{\A(.*):(\d+)\z}
    canonicalize_tcp($1, $2.to_i)
  else
    address
  end
end

#listen(address, options = {}) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/pitchfork/configurator.rb', line 217

def listen(address, options = {})
  address = expand_addr(address)
  if String === address
    [ :umask, :backlog, :sndbuf, :rcvbuf, :tries ].each do |key|
      value = options[key] or next
      Integer === value or
        raise ArgumentError, "not an integer: #{key}=#{value.inspect}"
    end
    [ :tcp_nodelay, :tcp_nopush, :ipv6only, :reuseport ].each do |key|
      (value = options[key]).nil? and next
      TrueClass === value || FalseClass === value or
        raise ArgumentError, "not boolean: #{key}=#{value.inspect}"
    end
    unless (value = options[:delay]).nil?
      Numeric === value or
        raise ArgumentError, "not numeric: delay=#{value.inspect}"
    end
    set[:listener_opts][address].merge!(options)
  end

  set[:listeners] << address
end

#listeners(addresses) ⇒ Object

sets listeners to the given addresses, replacing or augmenting the current set.



211
212
213
214
215
# File 'lib/pitchfork/configurator.rb', line 211

def listeners(addresses) # :nodoc:
  Array === addresses or addresses = Array(addresses)
  addresses.map! { |addr| expand_addr(addr) }
  set[:listeners] = addresses
end

#load(merge_defaults = true) ⇒ Object

:nodoc:



93
94
95
96
97
98
99
100
101
102
103
# File 'lib/pitchfork/configurator.rb', line 93

def load(merge_defaults = true) #:nodoc:
  if merge_defaults && @use_defaults
    set.merge!(DEFAULTS) if @use_defaults
  end
  instance_eval(File.read(config_file), config_file) if config_file

  parse_rackup_file

  RACKUP[:set_listener] and
    set[:listeners] << "#{RACKUP[:host]}:#{RACKUP[:port]}"
end

#logger(obj) ⇒ Object



129
130
131
132
133
134
135
136
# File 'lib/pitchfork/configurator.rb', line 129

def logger(obj)
  %w(debug info warn error fatal).each do |m|
    obj.respond_to?(m) and next
    raise ArgumentError, "logger=#{obj} does not respond to method=#{m}"
  end

  set[:logger] = obj
end

#refork_after(limits) ⇒ Object

Defines the number of requests per-worker after which a new generation should be spawned.

false can be used to mark a final generation, otherwise the last request count is re-used indefinitely.

example: . refork_after [50, 100, 1000] . refork_after [50, 100, 1000, false]

Note that reforking is only available on Linux. Other Unix-like systems don’t have this capability.



264
265
266
# File 'lib/pitchfork/configurator.rb', line 264

def refork_after(limits)
  set[:refork_condition] = ReforkCondition::RequestsCount.new(limits)
end

#rewindable_input(bool) ⇒ Object



240
241
242
# File 'lib/pitchfork/configurator.rb', line 240

def rewindable_input(bool)
  set_bool(:rewindable_input, bool)
end

#spawn_timeout(seconds) ⇒ Object



197
198
199
# File 'lib/pitchfork/configurator.rb', line 197

def spawn_timeout(seconds)
  set_int(:spawn_timeout, seconds, 1)
end

#timeout(seconds, cleanup: 2) ⇒ Object



178
179
180
181
182
# File 'lib/pitchfork/configurator.rb', line 178

def timeout(seconds, cleanup: 2)
  soft_timeout = set_int(:soft_timeout, seconds, 3)
  cleanup_timeout = set_int(:cleanup_timeout, cleanup, 2)
  set_int(:timeout, soft_timeout + cleanup_timeout, 5)
end

#timeout_signal(*args, &block) ⇒ Object



184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/pitchfork/configurator.rb', line 184

def timeout_signal(*args, &block)
  if block_given?
    set_hook(:timeout_signal, block, 1)
  elsif args.first.respond_to?(:call)
    set_hook(:timeout_signal, args.first, 1)
  elsif args.first.is_a?(Symbol)
    signal = args.first
    set_hook(:timeout_signal, ->(_pid) { signal }, 1)
  else
    raise ArgumentError, "timeout_signal must be a symbol or a proc"
  end
end

#worker_processes(nr) ⇒ Object



201
202
203
# File 'lib/pitchfork/configurator.rb', line 201

def worker_processes(nr)
  set_int(:worker_processes, nr, 1)
end