Module: Einhorn::Worker

Defined in:
lib/einhorn/worker.rb

Defined Under Namespace

Classes: WorkerError

Class Method Summary collapse

Class Method Details

.ack(*args) ⇒ Object



29
30
31
32
33
34
# File 'lib/einhorn/worker.rb', line 29

def self.ack(*args)
  begin
    ack!(*args)
  rescue WorkerError
  end
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['EINHORN_SOCK_PATH']
:fd:         Just use the file descriptor in ENV['EINHORN_SOCK_FD'].
             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.



68
69
70
71
72
# File 'lib/einhorn/worker.rb', line 68

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.



44
45
46
47
48
49
50
51
# File 'lib/einhorn/worker.rb', line 44

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



116
117
118
119
120
121
# File 'lib/einhorn/worker.rb', line 116

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

.einhorn_fd_countObject



123
124
125
126
127
128
# File 'lib/einhorn/worker.rb', line 123

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

.ensure_worker!Object



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

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.



131
132
133
# File 'lib/einhorn/worker.rb', line 131

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

.is_worker?Boolean

Returns:

  • (Boolean)


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

def self.is_worker?
  begin
    ensure_worker!
  rescue WorkerError
    false
  else
    true
  end
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['EINHORN_SOCK_PATH']
:fd:         Just use the file descriptor in ENV['EINHORN_SOCK_FD'].
             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.


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

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



93
94
95
96
# File 'lib/einhorn/worker.rb', line 93

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

.socket!(number = nil) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/einhorn/worker.rb', line 98

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