Module: Einhorn::Worker

Defined in:
lib/einhorn/worker.rb

Defined Under Namespace

Classes: WorkerError

Class Method Summary collapse

Class Method Details

.ack(*args) ⇒ Object



27
28
29
30
# File 'lib/einhorn/worker.rb', line 27

def self.ack(*args)
  ack!(*args)
rescue WorkerError
end

.ack!(discovery = :env, arg = nil) ⇒ Object

Call this once your app is up and running in a good state. Arguments:

@discovery: How to discover the master process's command socket. :env: Discover the path from ENV :fd: Just use the file descriptor in ENV. Must run the master with the -g flag. This is mostly useful if you don't have a nice library like Einhorn::Worker. Then @arg being true causes the FD to be left open after ACK; otherwise it is closed. :direct: Provide the path to the command socket in @arg.

TODO: add a :fileno option? Easy to implement; not sure if it'd be useful for anything. Maybe if it's always fd 3, because then the user wouldn't have to provide an arg.



64
65
66
67
68
# File 'lib/einhorn/worker.rb', line 64

def self.ack!(discovery = :env, arg = nil)
  handle_command_socket(discovery, arg) do |client|
    client.send_command("command" => "worker:ack", "pid" => $$)
  end
end

.einhorn_child_indexObject

Returns the index of this Einhorn child process.

If an Einhorn master has N children, this will be an integer in the range [0,N), and no two workers running concurrently will ever have the same index.

Returns nil if not running in Einhorn, or running on a version of Einhorn that does not support indexing children.



40
41
42
43
44
45
46
47
# File 'lib/einhorn/worker.rb', line 40

def self.einhorn_child_index
  index = ENV["EINHORN_CHILD_INDEX"]
  if index.nil? || index !~ /\A \d+ \z/x
    index
  else
    index.to_i
  end
end

.einhorn_fd(n) ⇒ Object



112
113
114
115
116
117
# File 'lib/einhorn/worker.rb', line 112

def self.einhorn_fd(n)
  unless (raw_fd = ENV["EINHORN_FD_#{n}"])
    return nil
  end
  Integer(raw_fd)
end

.einhorn_fd_countObject



119
120
121
122
123
124
# File 'lib/einhorn/worker.rb', line 119

def self.einhorn_fd_count
  unless (raw_count = ENV["EINHORN_FD_COUNT"])
    return 0
  end
  Integer(raw_count)
end

.ensure_worker!Object



16
17
18
19
20
21
22
23
24
25
# File 'lib/einhorn/worker.rb', line 16

def self.ensure_worker!
  # Make sure that EINHORN_MASTER_PID is my parent
  if (ppid_s = ENV["EINHORN_MASTER_PID"])
    ppid = ppid_s.to_i
    raise WorkerError.new("EINHORN_MASTER_PID environment variable is #{ppid_s.inspect}, but my parent's pid is #{Process.ppid.inspect}. This probably means that I am a subprocess of an Einhorn worker, but am not one myself.") unless Process.ppid == ppid
    true
  else
    raise WorkerError.new("No EINHORN_MASTER_PID environment variable set. Are you running your process under Einhorn?") unless Process.ppid == ppid
  end
end

.graceful_shutdown(&blk) ⇒ Object

Call this to handle graceful shutdown requests to your app.



127
128
129
# File 'lib/einhorn/worker.rb', line 127

def self.graceful_shutdown(&blk)
  Signal.trap("USR2", &blk)
end

.is_worker?Boolean

Returns:

  • (Boolean)


8
9
10
11
12
13
14
# File 'lib/einhorn/worker.rb', line 8

def self.is_worker?
  ensure_worker!
rescue WorkerError
  false
else
  true
end

.ping!(request_id, discovery = :env, arg = nil) ⇒ Object

Call this to indicate your child process is up and in a healthy state. Arguments:

@request_id: Identifies the request ID of the worker, can be used to debug wedged workers.

@discovery: How to discover the master process's command socket. :env: Discover the path from ENV :fd: Just use the file descriptor in ENV. Must run the master with the -g flag. This is mostly useful if you don't have a nice library like Einhorn::Worker. Then @arg being true causes the FD to be left open after ACK; otherwise it is closed. :direct: Provide the path to the command socket in @arg.



83
84
85
86
87
# File 'lib/einhorn/worker.rb', line 83

def self.ping!(request_id, discovery = :env, arg = nil)
  handle_command_socket(discovery, arg) do |client|
    client.send_command("command" => "worker:ping", "pid" => $$, "request_id" => request_id)
  end
end

.socket(number = nil) ⇒ Object



89
90
91
92
# File 'lib/einhorn/worker.rb', line 89

def self.socket(number = nil)
  number ||= 0
  einhorn_fd(number)
end

.socket!(number = nil) ⇒ Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/einhorn/worker.rb', line 94

def self.socket!(number = nil)
  number ||= 0

  unless (count = einhorn_fd_count)
    raise "No EINHORN_FD_COUNT provided in environment. Are you running under Einhorn?"
  end

  unless number < count
    raise "Only #{count} FDs available, but FD #{number} was requested"
  end

  unless (fd = einhorn_fd(number))
    raise "No EINHORN_FD_#{number} provided in environment. That's pretty weird"
  end

  fd
end