puma 3.12.0 → 5.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1413 -439
  3. data/LICENSE +23 -20
  4. data/README.md +131 -60
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +24 -19
  7. data/docs/compile_options.md +19 -0
  8. data/docs/deployment.md +38 -13
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +1 -1
  16. data/docs/plugins.md +20 -10
  17. data/docs/rails_dev_mode.md +29 -0
  18. data/docs/restart.md +47 -22
  19. data/docs/signals.md +7 -6
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +48 -70
  22. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  23. data/ext/puma_http11/ext_help.h +1 -1
  24. data/ext/puma_http11/extconf.rb +27 -0
  25. data/ext/puma_http11/http11_parser.c +84 -109
  26. data/ext/puma_http11/http11_parser.h +1 -1
  27. data/ext/puma_http11/http11_parser.java.rl +22 -38
  28. data/ext/puma_http11/http11_parser.rl +4 -2
  29. data/ext/puma_http11/http11_parser_common.rl +3 -3
  30. data/ext/puma_http11/mini_ssl.c +262 -87
  31. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  32. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  33. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
  34. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
  35. data/ext/puma_http11/puma_http11.c +34 -50
  36. data/lib/puma/app/status.rb +68 -49
  37. data/lib/puma/binder.rb +197 -144
  38. data/lib/puma/cli.rb +17 -15
  39. data/lib/puma/client.rb +257 -226
  40. data/lib/puma/cluster/worker.rb +176 -0
  41. data/lib/puma/cluster/worker_handle.rb +90 -0
  42. data/lib/puma/cluster.rb +223 -212
  43. data/lib/puma/commonlogger.rb +4 -2
  44. data/lib/puma/configuration.rb +58 -51
  45. data/lib/puma/const.rb +41 -19
  46. data/lib/puma/control_cli.rb +117 -73
  47. data/lib/puma/detect.rb +26 -3
  48. data/lib/puma/dsl.rb +531 -123
  49. data/lib/puma/error_logger.rb +104 -0
  50. data/lib/puma/events.rb +57 -31
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +2 -58
  53. data/lib/puma/json.rb +96 -0
  54. data/lib/puma/launcher.rb +182 -70
  55. data/lib/puma/minissl/context_builder.rb +79 -0
  56. data/lib/puma/minissl.rb +149 -48
  57. data/lib/puma/null_io.rb +15 -1
  58. data/lib/puma/plugin/tmp_restart.rb +2 -0
  59. data/lib/puma/plugin.rb +8 -12
  60. data/lib/puma/queue_close.rb +26 -0
  61. data/lib/puma/rack/builder.rb +4 -5
  62. data/lib/puma/rack/urlmap.rb +2 -0
  63. data/lib/puma/rack_default.rb +2 -0
  64. data/lib/puma/reactor.rb +87 -316
  65. data/lib/puma/request.rb +456 -0
  66. data/lib/puma/runner.rb +33 -52
  67. data/lib/puma/server.rb +288 -679
  68. data/lib/puma/single.rb +13 -67
  69. data/lib/puma/state_file.rb +10 -3
  70. data/lib/puma/systemd.rb +46 -0
  71. data/lib/puma/thread_pool.rb +131 -81
  72. data/lib/puma/util.rb +14 -6
  73. data/lib/puma.rb +54 -0
  74. data/lib/rack/handler/puma.rb +8 -6
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +45 -29
  78. data/ext/puma_http11/io_buffer.c +0 -155
  79. data/lib/puma/accept_nonblock.rb +0 -23
  80. data/lib/puma/compat.rb +0 -14
  81. data/lib/puma/convenient.rb +0 -23
  82. data/lib/puma/daemon_ext.rb +0 -31
  83. data/lib/puma/delegation.rb +0 -11
  84. data/lib/puma/java_io_buffer.rb +0 -45
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -39
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Puma::Runner
5
+ # This class is instantiated by the `Puma::Cluster` and represents a single
6
+ # worker process.
7
+ #
8
+ # At the core of this class is running an instance of `Puma::Server` which
9
+ # gets created via the `start_server` method from the `Puma::Runner` class
10
+ # that this inherits from.
11
+ class Worker < Puma::Runner
12
+ attr_reader :index, :master
13
+
14
+ def initialize(index:, master:, launcher:, pipes:, server: nil)
15
+ super launcher, launcher.events
16
+
17
+ @index = index
18
+ @master = master
19
+ @launcher = launcher
20
+ @options = launcher.options
21
+ @check_pipe = pipes[:check_pipe]
22
+ @worker_write = pipes[:worker_write]
23
+ @fork_pipe = pipes[:fork_pipe]
24
+ @wakeup = pipes[:wakeup]
25
+ @server = server
26
+ end
27
+
28
+ def run
29
+ title = "puma: cluster worker #{index}: #{master}"
30
+ title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
31
+ $0 = title
32
+
33
+ Signal.trap "SIGINT", "IGNORE"
34
+ Signal.trap "SIGCHLD", "DEFAULT"
35
+
36
+ Thread.new do
37
+ Puma.set_thread_name "worker check pipe"
38
+ IO.select [@check_pipe]
39
+ log "! Detected parent died, dying"
40
+ exit! 1
41
+ end
42
+
43
+ # If we're not running under a Bundler context, then
44
+ # report the info about the context we will be using
45
+ if !ENV['BUNDLE_GEMFILE']
46
+ if File.exist?("Gemfile")
47
+ log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
48
+ elsif File.exist?("gems.rb")
49
+ log "+ Gemfile in context: #{File.expand_path("gems.rb")}"
50
+ end
51
+ end
52
+
53
+ # Invoke any worker boot hooks so they can get
54
+ # things in shape before booting the app.
55
+ @launcher.config.run_hooks :before_worker_boot, index, @launcher.events
56
+
57
+ server = @server ||= start_server
58
+ restart_server = Queue.new << true << false
59
+
60
+ fork_worker = @options[:fork_worker] && index == 0
61
+
62
+ if fork_worker
63
+ restart_server.clear
64
+ worker_pids = []
65
+ Signal.trap "SIGCHLD" do
66
+ wakeup! if worker_pids.reject! do |p|
67
+ Process.wait(p, Process::WNOHANG) rescue true
68
+ end
69
+ end
70
+
71
+ Thread.new do
72
+ Puma.set_thread_name "worker fork pipe"
73
+ while (idx = @fork_pipe.gets)
74
+ idx = idx.to_i
75
+ if idx == -1 # stop server
76
+ if restart_server.length > 0
77
+ restart_server.clear
78
+ server.begin_restart(true)
79
+ @launcher.config.run_hooks :before_refork, nil, @launcher.events
80
+ Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
81
+ end
82
+ elsif idx == 0 # restart server
83
+ restart_server << true << false
84
+ else # fork worker
85
+ worker_pids << pid = spawn_worker(idx)
86
+ @worker_write << "f#{pid}:#{idx}\n" rescue nil
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ Signal.trap "SIGTERM" do
93
+ @worker_write << "e#{Process.pid}\n" rescue nil
94
+ restart_server.clear
95
+ server.stop
96
+ restart_server << false
97
+ end
98
+
99
+ begin
100
+ @worker_write << "b#{Process.pid}:#{index}\n"
101
+ rescue SystemCallError, IOError
102
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
103
+ STDERR.puts "Master seems to have exited, exiting."
104
+ return
105
+ end
106
+
107
+ while restart_server.pop
108
+ server_thread = server.run
109
+ stat_thread ||= Thread.new(@worker_write) do |io|
110
+ Puma.set_thread_name "stat payload"
111
+ base_payload = "p#{Process.pid}"
112
+
113
+ while true
114
+ begin
115
+ b = server.backlog || 0
116
+ r = server.running || 0
117
+ t = server.pool_capacity || 0
118
+ m = server.max_threads || 0
119
+ rc = server.requests_count || 0
120
+ payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
121
+ io << payload
122
+ rescue IOError
123
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
124
+ break
125
+ end
126
+ sleep Const::WORKER_CHECK_INTERVAL
127
+ end
128
+ end
129
+ server_thread.join
130
+ end
131
+
132
+ # Invoke any worker shutdown hooks so they can prevent the worker
133
+ # exiting until any background operations are completed
134
+ @launcher.config.run_hooks :before_worker_shutdown, index, @launcher.events
135
+ ensure
136
+ @worker_write << "t#{Process.pid}\n" rescue nil
137
+ @worker_write.close
138
+ end
139
+
140
+ private
141
+
142
+ def spawn_worker(idx)
143
+ @launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
144
+
145
+ pid = fork do
146
+ new_worker = Worker.new index: idx,
147
+ master: master,
148
+ launcher: @launcher,
149
+ pipes: { check_pipe: @check_pipe,
150
+ worker_write: @worker_write },
151
+ server: @server
152
+ new_worker.run
153
+ end
154
+
155
+ if !pid
156
+ log "! Complete inability to spawn new workers detected"
157
+ log "! Seppuku is the only choice."
158
+ exit! 1
159
+ end
160
+
161
+ @launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
162
+ pid
163
+ end
164
+
165
+ def wakeup!
166
+ return unless @wakeup
167
+
168
+ begin
169
+ @wakeup.write "!" unless @wakeup.closed?
170
+ rescue SystemCallError, IOError
171
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Runner
5
+ # This class represents a worker process from the perspective of the puma
6
+ # master process. It contains information about the process and its health
7
+ # and it exposes methods to control the process via IPC. It does not
8
+ # include the actual logic executed by the worker process itself. For that,
9
+ # see Puma::Cluster::Worker.
10
+ class WorkerHandle
11
+ def initialize(idx, pid, phase, options)
12
+ @index = idx
13
+ @pid = pid
14
+ @phase = phase
15
+ @stage = :started
16
+ @signal = "TERM"
17
+ @options = options
18
+ @first_term_sent = nil
19
+ @started_at = Time.now
20
+ @last_checkin = Time.now
21
+ @last_status = {}
22
+ @term = false
23
+ end
24
+
25
+ attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
26
+
27
+ # @version 5.0.0
28
+ attr_writer :pid, :phase
29
+
30
+ def booted?
31
+ @stage == :booted
32
+ end
33
+
34
+ def uptime
35
+ Time.now - started_at
36
+ end
37
+
38
+ def boot!
39
+ @last_checkin = Time.now
40
+ @stage = :booted
41
+ end
42
+
43
+ def term?
44
+ @term
45
+ end
46
+
47
+ def ping!(status)
48
+ @last_checkin = Time.now
49
+ captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
50
+ @last_status = captures.names.inject({}) do |hash, key|
51
+ hash[key.to_sym] = captures[key].to_i
52
+ hash
53
+ end
54
+ end
55
+
56
+ # @see Puma::Cluster#check_workers
57
+ # @version 5.0.0
58
+ def ping_timeout
59
+ @last_checkin +
60
+ (booted? ?
61
+ @options[:worker_timeout] :
62
+ @options[:worker_boot_timeout]
63
+ )
64
+ end
65
+
66
+ def term
67
+ begin
68
+ if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
69
+ @signal = "KILL"
70
+ else
71
+ @term ||= true
72
+ @first_term_sent ||= Time.now
73
+ end
74
+ Process.kill @signal, @pid if @pid
75
+ rescue Errno::ESRCH
76
+ end
77
+ end
78
+
79
+ def kill
80
+ @signal = 'KILL'
81
+ term
82
+ end
83
+
84
+ def hup
85
+ Process.kill "HUP", @pid
86
+ rescue Errno::ESRCH
87
+ end
88
+ end
89
+ end
90
+ end