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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/einhorn/worker.rb', line 68

def self.ack!(discovery=:env, arg=nil)
  ensure_worker!
  close_after_use = true

  case discovery
  when :env
    socket = ENV['EINHORN_SOCK_PATH']
    client = Einhorn::Client.for_path(socket)
  when :fd
    raise "No EINHORN_SOCK_FD provided in environment. Did you run einhorn with the -g flag?" unless fd_str = ENV['EINHORN_SOCK_FD']

    fd = Integer(fd_str)
    client = Einhorn::Client.for_fd(fd)
    close_after_use = false if arg
  when :direct
    socket = arg
    client = Einhorn::Client.for_path(socket)
  else
    raise "Unrecognized socket discovery mechanism: #{discovery.inspect}. Must be one of :filesystem, :argv, or :direct"
  end

  client.send_command({
      'command' => 'worker:ack',
      'pid' => $$
    })

  client.close if close_after_use
  true
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


121
122
123
124
125
126
# File 'lib/einhorn/worker.rb', line 121

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

.einhorn_fd_countObject


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

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.


136
137
138
# File 'lib/einhorn/worker.rb', line 136

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

.socket(number = nil) ⇒ Object


98
99
100
101
# File 'lib/einhorn/worker.rb', line 98

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

.socket!(number = nil) ⇒ Object


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/einhorn/worker.rb', line 103

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