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