uc 0.0.7 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/uc/server.rb CHANGED
@@ -1,57 +1,49 @@
1
1
  require 'uc/logger'
2
2
  require 'uc/shell_helper'
3
- require 'uc/mqueue'
4
- require 'uc/unicorn/api'
3
+ require 'uc/config'
4
+ require 'uc/status'
5
5
  require 'uc/unicorn/config'
6
+ require 'uc/unicorn/paths'
6
7
  require 'uc/error'
8
+ require 'uc/paths'
7
9
 
8
10
  module Uc
9
-
10
11
  class Server
11
12
 
12
13
  include ::Uc::ShellHelper
13
14
  include ::Uc::Logger
14
15
 
15
- attr_reader :uconfig
16
- attr_reader :rails_env
16
+ attr_reader :paths, :rails_env, :app_dir
17
17
 
18
- def initialize(app_dir, rails_env: "production")
19
- @uconfig = ::Uc::Unicorn::Config.new(app_dir)
18
+ def initialize(app_dir, rails_env: "production", debug: false)
19
+ @app_dir = app_dir
20
20
  @rails_env = rails_env
21
- @uconfig.load_env
22
- end
23
-
24
- def app_env(&block)
25
- uconfig.dirs_checked? || uconfig.check_dirs
26
- yield if block
21
+ @debug = debug
27
22
  end
28
23
 
29
24
  def start
30
- app_env do
31
- if server_running?
32
- logger.info "unicorn already running pid #{pid}"
33
- return
34
- end
35
-
36
- cmd %{unicorn -c #{uconfig.config_path} -D -E #{rails_env} }, return_output: false,
25
+ init_once
26
+ if server_status.running?
27
+ puts server_status
28
+ return
29
+ end
30
+ event_stream.watch_in_background :fin do
31
+ cmd %{unicorn -c #{uconfig.path} -D -E #{rails_env} }, return_output: false,
37
32
  error_msg: "error starting unicorn"
38
33
  end
39
34
  end
40
35
 
41
36
  def stop
42
- app_env do
43
- if not server_running?
44
- puts "unicorn not running"
45
- return
46
- end
47
-
48
- kill(pid, 30)
37
+ init_once
38
+ if server_status.stopped?
39
+ logger.info "unicorn not running"
40
+ return
49
41
  end
42
+ kill(server_status.pid, 30)
50
43
  end
51
44
 
52
45
  def status
53
- status = ( server_running? ? "Running pid #{pid}" : "Stopped")
54
- puts status
46
+ puts server_status
55
47
  end
56
48
 
57
49
  def restart
@@ -60,65 +52,62 @@ module Uc
60
52
  end
61
53
 
62
54
  def rolling_restart
63
- app_env
64
- if not server_running?
55
+ init_once
56
+ uconfig.generate_once
57
+ if not server_status.running?
65
58
  start
66
59
  return
67
60
  end
68
- mq = ::Uc::Mqueue.new(queue_name)
69
- begin
70
- mq.watch :fin do
71
- Process.kill("USR2", pid)
72
- end
73
- rescue Errno::EACCES
74
- raise ::Uc::Error, "unable to setup message queue"
75
- rescue Errno::ENOENT
76
- raise ::Uc::Error, "message queue deleted"
77
- rescue Errno::ETIMEDOUT
78
- raise ::Uc::Error, "timeout reached while waiting for server to restart"
61
+ event_stream.watch :fin do
62
+ Process.kill("USR2", server_status.pid)
63
+ end
64
+ end
65
+
66
+ def print_config
67
+ init_once
68
+ config.to_h.each do |k,v|
69
+ v = %{ "#{v}" } if not v.is_a? Numeric
70
+ puts "#{k} #{v}"
79
71
  end
80
72
  end
81
73
 
82
74
  private
83
75
 
84
- def kill(pid, timeout)
85
- Process.kill(:TERM, pid)
86
- logger.debug "TERM signal sent to #{pid}"
87
- (1..timeout).each do
88
- if not process_running? pid
89
- logger.info "Stopped #{pid}"
90
- return
91
- end
92
- sleep 1
93
- end
94
- Process.kill(9, pid)
95
- sleep 1
96
- logger.info "Killed #{pid}"
76
+ def server_status
77
+ @server_status ||= ::Uc::Status.new(unicorn_paths)
97
78
  end
98
79
 
80
+ def paths
81
+ @paths ||= ::Uc::Paths.new(app_dir)
82
+ end
99
83
 
100
- def process_running?(pid)
101
- return false if pid <= 0
102
- Process.getpgid pid
103
- return true
104
- rescue Errno::ESRCH
105
- return false
84
+ def config
85
+ @config ||= ::Uc::Config.new(app_dir)
106
86
  end
107
87
 
108
- def server_running?
109
- process_running? pid
88
+ def unicorn_paths
89
+ @unicorn_paths ||= ::Uc::Unicorn::Paths.new(config.app_dir)
110
90
  end
111
91
 
112
- def pid
113
- uconfig.pid
92
+ def uconfig
93
+ @uconfig ||= ::Uc::Unicorn::Config.new(config.to_h, unicorn_paths)
114
94
  end
115
95
 
116
- def queue_name
117
- @queue_name ||= Dir.chdir uconfig.app_dir do
118
- api = ::Uc::Unicorn::Api.new
119
- api.queue_name
120
- end
96
+ def lock
97
+ @lock ||= ::Uc::Lock.new(app_dir)
98
+ end
99
+
100
+ def init
101
+ paths.validate_required
102
+ lock.acquire
103
+ ::Uc::Logger.event_queue = config.event_queue_name
104
+ config.load_env
105
+ event_stream.debug_output = true if @debug
106
+ end
107
+
108
+ def init_once
109
+ @init_once ||= init
121
110
  end
122
111
 
123
- end
112
+ end
124
113
  end
@@ -2,7 +2,7 @@ module Uc
2
2
  module ShellHelper
3
3
 
4
4
  def cmd(command, error_msg: nil, return_output: false)
5
- puts "Running #{command}"
5
+ puts "#{"Running".bold.green} #{command}"
6
6
  if return_output
7
7
  output = `#{command}`
8
8
  else
@@ -15,5 +15,29 @@ module Uc
15
15
  return output
16
16
  end
17
17
 
18
+ def kill(pid, timeout)
19
+ Process.kill(:TERM, pid)
20
+ logger.debug "TERM signal sent to #{pid}"
21
+ (1..timeout).each do
22
+ if not process_running? pid
23
+ logger.info "Stopped #{pid}"
24
+ return
25
+ end
26
+ sleep 1
27
+ end
28
+ Process.kill(9, pid)
29
+ sleep 1
30
+ logger.info "Killed #{pid}"
31
+ end
32
+
33
+ def process_running?(pid)
34
+ return false if pid <= 0
35
+ Process.getpgid pid
36
+ return true
37
+ rescue Errno::ESRCH
38
+ return false
39
+ end
40
+
41
+
18
42
  end
19
43
  end
data/lib/uc/status.rb ADDED
@@ -0,0 +1,49 @@
1
+ require 'uc/shell_helper'
2
+
3
+ module Uc
4
+ class Status
5
+
6
+ include ::Uc::ShellHelper
7
+
8
+ attr_reader :paths
9
+ attr_accessor :use_pid
10
+
11
+ def initialize(unicorn_paths)
12
+ @paths = unicorn_paths
13
+ end
14
+
15
+ def running?
16
+ return process_running? pid if use_pid
17
+ not ex_lock_available?
18
+ end
19
+
20
+ def pid
21
+ @pid ||= read_pid
22
+ end
23
+
24
+ def stopped?
25
+ not running?
26
+ end
27
+
28
+ def to_s
29
+ status = ( running? ? "Running pid #{pid}" : "Stopped")
30
+ end
31
+
32
+ private
33
+
34
+ def ex_lock_available?
35
+ File.open( paths.lock_file, 'a+') do |f|
36
+ ex_lock = f.flock(File::LOCK_EX|File::LOCK_NB)
37
+ end
38
+ end
39
+
40
+ def read_pid
41
+ pid = (File.read paths.pid_file).to_i
42
+ pid == 0 ? -1 : pid
43
+ rescue
44
+ return -1
45
+ end
46
+
47
+
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ require 'uc/unicorn/api'
2
+ uc = ::Uc::Unicorn::Api.new("<%= config.fetch :event_queue %>")
3
+ worker_processes <%= config.fetch :instances %>
4
+ working_directory "<%= paths.app_dir %>"
5
+ timeout <%= config.fetch :timeout %>
6
+ listen "<%= paths.socket %>" , :backlog => <%= config.fetch :queue_size %>
7
+ pid "<%= paths.pid_file %>"
8
+ stdout_path "<%= paths.stdout_log %>"
9
+ stderr_path "<%= paths.stderr_log %>"
10
+
11
+ ready_wait = <%= config.fetch :ready_wait %>
12
+
13
+ preload_app true
14
+
15
+ GC.respond_to?(:copy_on_write_friendly=) and
16
+ GC.copy_on_write_friendly = true
17
+
18
+ check_client_connection false
19
+
20
+ before_fork do |server, worker|
21
+
22
+ uc.init(server)
23
+
24
+ defined?(ActiveRecord::Base) and
25
+ ActiveRecord::Base.connection.disconnect!
26
+
27
+ if defined?(Resque)
28
+ Resque.redis.quit
29
+ Rails.logger.info('Disconnected from Redis')
30
+ end
31
+
32
+ uc.wait_for_worker_ready(server, worker, ready_wait: ready_wait)
33
+ uc.gradual_shutdown(server, worker)
34
+ sleep 0.3
35
+ end
36
+
37
+ after_fork do |server, worker|
38
+ defined?(ActiveRecord::Base) and
39
+ ActiveRecord::Base.establish_connection
40
+
41
+ uc.prestart server, worker, url: "<%= config.fetch :prestart_url %>"
42
+ uc.send_worker_ready server, worker, ready_wait: ready_wait
43
+ uc.end_run worker
44
+ end
45
+
46
+ before_exec do |server|
47
+ uc.clean_env
48
+ uc.load_env
49
+ end
50
+
51
+ if respond_to? :on_exec_fail
52
+ uc = uc
53
+ on_exec_fail do |server|
54
+ puts "runnign asdadad"
55
+ uc.on_exec_fail
56
+ puts "lol"
57
+ end
58
+ end
@@ -1,30 +1,71 @@
1
- require 'uc/unicorn/rolling_restart'
1
+ require 'uc/config'
2
+ require 'uc/unicorn/init'
3
+ require 'uc/unicorn/gradual_shutdown'
2
4
  require 'uc/unicorn/prestart'
5
+ require 'uc/unicorn/ready_event'
6
+ require 'uc/unicorn/ready_wait'
3
7
  require 'securerandom'
8
+ require 'uc/logger'
4
9
 
5
10
  module Uc; module Unicorn
6
11
 
7
12
  class Api
8
13
 
9
14
  attr_reader :run_id
15
+ attr_accessor :queue_name
10
16
 
11
- def initialize
17
+ def initialize(event_queue)
18
+ @queue_name = event_queue
12
19
  @run_id = SecureRandom.hex(3)
20
+ ::Uc::Logger.event_queue = queue_name
13
21
  end
14
-
15
- def rolling_restart(server, worker, **kwargs)
16
- rolling_restart = ::Uc::Unicorn::RollingRestart.new(server, worker, queue_name, **kwargs)
17
- rolling_restart.run_id = @run_id
18
- rolling_restart.run
22
+
23
+ def init(server)
24
+ @init ||= ::Uc::Unicorn::Init.new(server)
25
+ @init.run_once
19
26
  end
20
27
 
28
+ def gradual_shutdown(server, worker)
29
+ gradual_shutdown = ::Uc::Unicorn::GradualShutdown.new(server, worker)
30
+ gradual_shutdown.run
31
+ end
21
32
 
22
33
  def prestart(server, worker, **kwargs)
23
- prestart = ::Uc::Unicorn::Prestart.new(server, worker, queue_name, **kwargs)
24
- prestart.run_id = @run_id
34
+ prestart = ::Uc::Unicorn::Prestart.new(server, worker, **kwargs)
25
35
  prestart.run
26
36
  end
27
37
 
38
+ def send_worker_ready(server, worker, **kwargs)
39
+ ready_event = ::Uc::Unicorn::ReadyEvent.new(server, worker, **kwargs)
40
+ ready_event.run_id = run_id
41
+ ready_event.run
42
+ end
43
+
44
+ def wait_for_worker_ready(server, worker, **kwargs)
45
+ ready_event_wait = ::Uc::Unicorn::ReadyWait.new(server, worker, **kwargs)
46
+ ready_event_wait.run_id = run_id
47
+ ready_event_wait.run
48
+ end
49
+
50
+ def acquire_shared_lock(lock_file)
51
+ shared_lock = ::Uc::Unicorn::Lock.new(lock_file)
52
+ shared_lock.acquire
53
+ end
54
+
55
+ def shared_env
56
+ @shared_env ||= {}
57
+ end
58
+
59
+ def end_run(worker)
60
+ @init.end_run(worker)
61
+ end
62
+
63
+ def on_exec_fail
64
+ event_stream = ::Uc::Logger.event_stream
65
+ event_stream.close_connections
66
+ event_stream.fatal "re-exec failed"
67
+ end
68
+
28
69
  def clean_env
29
70
  ENV.delete "BUNDLE_BIN_PATH"
30
71
  ENV.delete "RUBYLIB"
@@ -33,26 +74,9 @@ module Uc; module Unicorn
33
74
  ENV.delete "GEM_PATH"
34
75
  end
35
76
 
36
- def queue_name
37
- @queue_name ||= begin
38
- queue_name_from_file || "unicorn_#{Process.uid}"
39
- end
40
- end
41
-
42
- private
43
-
44
- def queue_name_from_file
45
- queue_file = Pathname.new queue_name_file
46
- queue_name = nil
47
- if queue_file.readable?
48
- queue_name = File.read(queue_file).chomp
49
- queue_name = (queue_name.empty? ? nil : queue_name)
50
- end
51
- return queue_name
52
- end
53
-
54
- def queue_name_file
55
- "config/unicorn_mq"
77
+ def load_env
78
+ config = ::Uc::Config.new(Dir.pwd)
79
+ config.load_env
56
80
  end
57
81
 
58
82
  end
@@ -1,86 +1,46 @@
1
- require 'pathname'
2
1
  require 'uc/logger'
3
- module Uc; module Unicorn
4
- class Config
2
+ require 'uc/error'
3
+ require 'erb'
5
4
 
6
- include ::Uc::Logger
5
+ module Uc
6
+ module Unicorn
7
7
 
8
- attr_reader :app_dir
8
+ class Config
9
9
 
10
- def initialize(app_dir)
11
- @app_dir = app_dir
12
- end
13
-
14
- def config_path
15
- rpath "config/unicorn.rb"
16
- end
17
-
18
-
19
- def stdout_log
20
- rpath "log/unicorn.stdout.log"
21
- end
22
-
23
- def stderr_log
24
- rpath "log/unicorn.stderr.log"
25
- end
26
-
27
- def socket_file
28
- rpath "tmp/sockets/unicorn.sock"
29
- end
30
-
31
- def pid_file
32
- rpath "tmp/pids/unicorn.pid"
33
- end
10
+ include ::Uc::Logger
34
11
 
35
- def rack_path
36
- rpath "config.ru"
37
- end
38
-
39
- def read_pid
40
- pid = (File.read pid_file).to_i
41
- pid == 0 ? -1 : pid
42
- rescue
43
- return -1
44
- end
12
+ attr_reader :paths, :config
45
13
 
46
- def pid
47
- @pid ||= read_pid
48
- end
14
+ def initialize(config_hash, paths)
15
+ @config = config_hash
16
+ @paths = paths
17
+ end
49
18
 
50
- def check_dirs
51
- logger.debug "Using app_dir => #{app_dir}"
52
- raise ::Uc::Error, %{app_dir not readable} if not path_readable? app_dir
53
- Dir.chdir app_dir do
54
- raise ::Uc::Error, %{no config.ru found in app_dir} if not path_readable? rack_path
55
- raise ::Uc::Error, %{no unicorn config found %} if not path_readable? config_path
19
+ def path
20
+ generate_once
21
+ paths.unicorn_config
56
22
  end
57
- @dirs_checked = true
58
- end
59
-
60
- def dirs_checked?
61
- @dirs_checked
62
- end
63
23
 
64
- def load_env
65
- env_file = rpath "config/uc_env.rb"
66
- File.readable? env_file and
67
- load env_file
68
- rescue LoadError
69
- end
24
+ def generate_config_file
25
+ erb = ERB.new(File.read(paths.unicorn_template))
26
+ binding = Kernel.binding
27
+ File.open(paths.unicorn_config, 'w') do |f|
28
+ f.write erb.result(binding)
29
+ end
30
+ return true
31
+ rescue => e
32
+ logger.debug e.message
33
+ raise ::Uc::Error, "unable to generate unicorn config"
34
+ end
70
35
 
71
- private
36
+ def generate_once
37
+ return if @config_generated
38
+ generate_config_file
39
+ @config_generated = true
40
+ end
72
41
 
73
- def rpath(path)
74
- path = Pathname.new(path)
75
- raise "absolute path specified: #{path}" if path.absolute?
76
- "#{app_dir}/#{path}"
77
- end
78
42
 
79
- def path_readable?(path_str)
80
- path = Pathname.new(path_str)
81
- path.readable?
82
43
  end
83
44
 
84
-
85
- end
86
- end; end
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ require 'uc/logger'
2
+ require 'uc/unicorn/helper'
3
+
4
+ module Uc
5
+ module Unicorn
6
+
7
+ class GradualShutdown
8
+
9
+ include ::Uc::Logger
10
+ include ::Uc::Unicorn::Helper
11
+
12
+ attr_reader :server, :worker
13
+
14
+ def initialize(server, worker)
15
+ @server = server
16
+ @worker = worker
17
+ end
18
+
19
+ def run
20
+ return if not (server && worker)
21
+ return if not restart?
22
+ shutdown_old_master
23
+ end
24
+
25
+ def shutdown_old_master
26
+ event_stream.debug "stopping old worker #{id}"
27
+ if send_signal
28
+ event_stream.debug "stopped old worker #{id}"
29
+ end
30
+ end
31
+
32
+ def sig
33
+ (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
34
+ end
35
+
36
+ def send_signal
37
+ Process.kill(sig, File.read(old_pid).to_i)
38
+ return true
39
+ rescue => e
40
+ log_kill_error e
41
+ return false
42
+ end
43
+
44
+ def log_kill_error(e)
45
+ stderr.info "error sending kill signal #{id}| #{e.class} #{e.message}"
46
+ event_stream.warn "error while stopping worker #{worker.nr + 1}, #{e.class}"
47
+ end
48
+
49
+ def id
50
+ @id ||= (worker.nr + 1)
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+ end
57
+
@@ -0,0 +1,25 @@
1
+ module Uc
2
+ module Unicorn
3
+ module Helper
4
+
5
+ def restart?
6
+ (File.readable? old_pid) && (server.pid != old_pid)
7
+ end
8
+
9
+ def first_worker?
10
+ worker.nr == 0
11
+ end
12
+
13
+ def last_worker?
14
+ (worker.nr + 1) == server.worker_processes
15
+ end
16
+
17
+ def old_pid
18
+ "#{server.config[:pid]}.oldbin"
19
+ end
20
+
21
+
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,58 @@
1
+ require 'uc/unicorn/helper'
2
+ require 'uc/error'
3
+ require 'uc/logger'
4
+
5
+ module Uc
6
+ module Unicorn
7
+ class Init
8
+
9
+ include ::Uc::Logger
10
+ include ::Uc::Unicorn::Helper
11
+
12
+ attr_accessor :server, :worker
13
+
14
+ def initialize(server)
15
+ @server = server
16
+ end
17
+
18
+ def event_type
19
+ @event_type ||= (restart? ? "restart" : "start")
20
+ end
21
+
22
+ def lock_file
23
+ @lock_file ||= "tmp/unicorn.lock"
24
+ end
25
+
26
+ def run
27
+ event_stream.debug "event_type #{event_type}"
28
+ acquired = acquire_lock
29
+ if not acquired
30
+ error_msg = "unable to acquire shared lock (unicorn)"
31
+ event_stream.fatal error_msg
32
+ raise ::Uc::Error, error_msg
33
+ end
34
+ end
35
+
36
+ def acquire_lock
37
+ lock_acquired = File.new(lock_file, "a+").flock(File::LOCK_SH | File::LOCK_NB )
38
+ rescue => e
39
+ stderr.error "#{e.class} #{e.message}\n #{e.backtrace.join("\n")}"
40
+ return false
41
+ end
42
+
43
+ def run_once
44
+ return if @ran_once
45
+ @ran_once = true
46
+ run
47
+ end
48
+
49
+ def end_run(worker)
50
+ @worker = worker
51
+ if last_worker?
52
+ event_stream.pub :fin, "server #{event_type}"
53
+ end
54
+ end
55
+
56
+ end
57
+ end
58
+ end