pitchfork 0.13.0 → 0.14.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +6 -2
- data/.ruby-version +1 -0
- data/CHANGELOG.md +6 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +2 -4
- data/docs/CONFIGURATION.md +38 -0
- data/docs/MIGRATING_FROM_UNICORN.md +34 -0
- data/docs/WHY_MIGRATE.md +5 -0
- data/examples/pitchfork.conf.service.rb +27 -0
- data/exe/pitchfork +4 -4
- data/ext/pitchfork_http/extconf.rb +2 -0
- data/ext/pitchfork_http/memory_page.c +223 -0
- data/ext/pitchfork_http/pitchfork_http.c +213 -211
- data/ext/pitchfork_http/pitchfork_http.rl +3 -1
- data/lib/pitchfork/children.rb +20 -15
- data/lib/pitchfork/configurator.rb +12 -0
- data/lib/pitchfork/http_parser.rb +11 -66
- data/lib/pitchfork/http_response.rb +1 -1
- data/lib/pitchfork/http_server.rb +175 -59
- data/lib/pitchfork/message.rb +10 -6
- data/lib/pitchfork/shared_memory.rb +16 -14
- data/lib/pitchfork/socket_helper.rb +1 -1
- data/lib/pitchfork/version.rb +1 -1
- data/lib/pitchfork/worker.rb +43 -15
- data/lib/pitchfork.rb +0 -20
- data/pitchfork.gemspec +0 -1
- metadata +7 -18
- data/lib/pitchfork/app/old_rails/static.rb +0 -60
@@ -1,40 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'raindrops'
|
4
|
-
|
5
3
|
module Pitchfork
|
6
4
|
module SharedMemory
|
7
5
|
extend self
|
8
6
|
|
9
|
-
PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
|
10
7
|
CURRENT_GENERATION_OFFSET = 0
|
11
8
|
SHUTDOWN_OFFSET = 1
|
12
9
|
MOLD_TICK_OFFSET = 2
|
13
10
|
MOLD_PROMOTION_TICK_OFFSET = 3
|
14
|
-
|
11
|
+
SERVICE_TICK_OFFSET = 4
|
12
|
+
WORKER_TICK_OFFSET = 5
|
15
13
|
|
16
|
-
|
14
|
+
PAGES = [MemoryPage.new(MemoryPage::SLOTS)]
|
17
15
|
|
18
16
|
def current_generation
|
19
|
-
|
17
|
+
PAGES[0][CURRENT_GENERATION_OFFSET]
|
20
18
|
end
|
21
19
|
|
22
20
|
def current_generation=(value)
|
23
|
-
|
21
|
+
PAGES[0][CURRENT_GENERATION_OFFSET] = value
|
24
22
|
end
|
25
23
|
|
26
24
|
def shutting_down!
|
27
|
-
|
25
|
+
PAGES[0][SHUTDOWN_OFFSET] = 1
|
28
26
|
end
|
29
27
|
|
30
28
|
def shutting_down?
|
31
|
-
|
29
|
+
PAGES[0][SHUTDOWN_OFFSET] > 0
|
32
30
|
end
|
33
31
|
|
34
32
|
class Field
|
35
33
|
def initialize(offset)
|
36
|
-
@drop =
|
37
|
-
@offset = offset %
|
34
|
+
@drop = PAGES.fetch(offset / MemoryPage::SLOTS)
|
35
|
+
@offset = offset % MemoryPage::SLOTS
|
38
36
|
end
|
39
37
|
|
40
38
|
def value
|
@@ -54,6 +52,10 @@ module Pitchfork
|
|
54
52
|
self[MOLD_PROMOTION_TICK_OFFSET]
|
55
53
|
end
|
56
54
|
|
55
|
+
def service_deadline
|
56
|
+
self[SERVICE_TICK_OFFSET]
|
57
|
+
end
|
58
|
+
|
57
59
|
def worker_deadline(worker_nr)
|
58
60
|
self[WORKER_TICK_OFFSET + worker_nr]
|
59
61
|
end
|
@@ -67,9 +69,9 @@ module Pitchfork
|
|
67
69
|
#
|
68
70
|
# However this doesn't account for TTIN signals that increase the
|
69
71
|
# number of workers, but we should probably remove that feature too.
|
70
|
-
def
|
71
|
-
0.upto(((WORKER_TICK_OFFSET + workers_count) /
|
72
|
-
|
72
|
+
def preallocate_pages(workers_count)
|
73
|
+
0.upto(((WORKER_TICK_OFFSET + workers_count) / MemoryPage::SLOTS.to_f).ceil) do |i|
|
74
|
+
PAGES[i] ||= MemoryPage.new(MemoryPage::SLOTS)
|
73
75
|
end
|
74
76
|
end
|
75
77
|
end
|
@@ -72,7 +72,7 @@ module Pitchfork
|
|
72
72
|
rescue => e
|
73
73
|
logger.error("#{sock_name(sock)} " \
|
74
74
|
"failed to set accept_filter=#{name} (#{e.inspect})")
|
75
|
-
logger.error("perhaps accf_http(9) needs to be loaded"
|
75
|
+
logger.error("perhaps accf_http(9) needs to be loaded")
|
76
76
|
end if arg != got
|
77
77
|
end
|
78
78
|
end
|
data/lib/pitchfork/version.rb
CHANGED
data/lib/pitchfork/worker.rb
CHANGED
@@ -6,7 +6,7 @@ module Pitchfork
|
|
6
6
|
# This class and its members can be considered a stable interface
|
7
7
|
# and will not change in a backwards-incompatible fashion between
|
8
8
|
# releases of pitchfork. Knowledge of this class is generally not
|
9
|
-
#
|
9
|
+
# needed for most users of pitchfork.
|
10
10
|
#
|
11
11
|
# Some users may want to access it in the after_worker_fork/after_mold_fork hooks.
|
12
12
|
# See the Pitchfork::Configurator RDoc for examples.
|
@@ -24,20 +24,7 @@ module Pitchfork
|
|
24
24
|
@to_io = @master = nil
|
25
25
|
@exiting = false
|
26
26
|
@requests_count = 0
|
27
|
-
|
28
|
-
@deadline_drop = SharedMemory.worker_deadline(nr)
|
29
|
-
self.deadline = 0
|
30
|
-
else
|
31
|
-
promoted!(nil)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def meminfo
|
36
|
-
@meminfo ||= MemInfo.new(pid) if pid
|
37
|
-
end
|
38
|
-
|
39
|
-
def refresh
|
40
|
-
meminfo&.update
|
27
|
+
init_deadline
|
41
28
|
end
|
42
29
|
|
43
30
|
def exiting?
|
@@ -94,6 +81,10 @@ module Pitchfork
|
|
94
81
|
send_message_nonblock(Message::SpawnWorker.new(new_worker.nr))
|
95
82
|
end
|
96
83
|
|
84
|
+
def spawn_service(_new_service)
|
85
|
+
send_message_nonblock(Message::SpawnService.new)
|
86
|
+
end
|
87
|
+
|
97
88
|
def promote!(timeout)
|
98
89
|
@generation += 1
|
99
90
|
promoted!(timeout)
|
@@ -111,6 +102,10 @@ module Pitchfork
|
|
111
102
|
@mold
|
112
103
|
end
|
113
104
|
|
105
|
+
def service?
|
106
|
+
false
|
107
|
+
end
|
108
|
+
|
114
109
|
def to_io # IO.select-compatible
|
115
110
|
@to_io.to_io
|
116
111
|
end
|
@@ -220,6 +215,15 @@ module Pitchfork
|
|
220
215
|
|
221
216
|
private
|
222
217
|
|
218
|
+
def init_deadline
|
219
|
+
if nr
|
220
|
+
@deadline_drop = SharedMemory.worker_deadline(@nr)
|
221
|
+
self.deadline = 0
|
222
|
+
else
|
223
|
+
promoted!(nil)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
223
227
|
def pipe=(socket)
|
224
228
|
raise ArgumentError, "pipe can't be nil" unless socket
|
225
229
|
Info.keep_io(socket)
|
@@ -241,4 +245,28 @@ module Pitchfork
|
|
241
245
|
success
|
242
246
|
end
|
243
247
|
end
|
248
|
+
|
249
|
+
class Service < Worker
|
250
|
+
def initialize(pid: nil, generation: 0)
|
251
|
+
super(nil, pid: pid, generation: generation)
|
252
|
+
end
|
253
|
+
|
254
|
+
def service?
|
255
|
+
true
|
256
|
+
end
|
257
|
+
|
258
|
+
def register_to_master(control_socket)
|
259
|
+
create_socketpair!
|
260
|
+
message = Message::ServiceSpawned.new(@pid, generation, @master)
|
261
|
+
control_socket.sendmsg(message)
|
262
|
+
@master.close
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def init_deadline
|
268
|
+
@deadline_drop = SharedMemory.service_deadline
|
269
|
+
self.deadline = 0
|
270
|
+
end
|
271
|
+
end
|
244
272
|
end
|
data/lib/pitchfork.rb
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
# frozen_string_literal: true
|
3
3
|
require 'etc'
|
4
4
|
require 'stringio'
|
5
|
-
require 'raindrops'
|
6
5
|
require 'io/wait'
|
7
6
|
|
8
7
|
begin
|
@@ -120,25 +119,6 @@ module Pitchfork
|
|
120
119
|
exc.backtrace.each { |line| logger.error(line) }
|
121
120
|
end
|
122
121
|
|
123
|
-
F_SETPIPE_SZ = 1031 if RUBY_PLATFORM =~ /linux/
|
124
|
-
|
125
|
-
def pipe # :nodoc:
|
126
|
-
IO.pipe.each do |io|
|
127
|
-
# shrink pipes to minimize impact on /proc/sys/fs/pipe-user-pages-soft
|
128
|
-
# limits.
|
129
|
-
if defined?(F_SETPIPE_SZ)
|
130
|
-
begin
|
131
|
-
io.fcntl(F_SETPIPE_SZ, Raindrops::PAGE_SIZE)
|
132
|
-
rescue Errno::EINVAL
|
133
|
-
# old kernel
|
134
|
-
rescue Errno::EPERM
|
135
|
-
# resizes fail if Linux is close to the pipe limit for the user
|
136
|
-
# or if the user does not have permissions to resize
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
122
|
def socketpair
|
143
123
|
pair = UNIXSocket.socketpair(@socket_type).map { |s| MessageSocket.new(s) }
|
144
124
|
pair[0].close_write
|
data/pitchfork.gemspec
CHANGED
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pitchfork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-05-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: raindrops
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0.7'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0.7'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: rack
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,6 +40,7 @@ files:
|
|
54
40
|
- ".gitattributes"
|
55
41
|
- ".github/workflows/ci.yml"
|
56
42
|
- ".gitignore"
|
43
|
+
- ".ruby-version"
|
57
44
|
- CHANGELOG.md
|
58
45
|
- COPYING
|
59
46
|
- Dockerfile
|
@@ -68,6 +55,7 @@ files:
|
|
68
55
|
- docs/CONFIGURATION.md
|
69
56
|
- docs/DESIGN.md
|
70
57
|
- docs/FORK_SAFETY.md
|
58
|
+
- docs/MIGRATING_FROM_UNICORN.md
|
71
59
|
- docs/PHILOSOPHY.md
|
72
60
|
- docs/REFORKING.md
|
73
61
|
- docs/SIGNALS.md
|
@@ -79,6 +67,7 @@ files:
|
|
79
67
|
- examples/nginx.conf
|
80
68
|
- examples/pitchfork.conf.minimal.rb
|
81
69
|
- examples/pitchfork.conf.rb
|
70
|
+
- examples/pitchfork.conf.service.rb
|
82
71
|
- examples/unicorn.socket
|
83
72
|
- exe/pitchfork
|
84
73
|
- ext/pitchfork_http/CFLAGS
|
@@ -90,11 +79,11 @@ files:
|
|
90
79
|
- ext/pitchfork_http/extconf.rb
|
91
80
|
- ext/pitchfork_http/global_variables.h
|
92
81
|
- ext/pitchfork_http/httpdate.c
|
82
|
+
- ext/pitchfork_http/memory_page.c
|
93
83
|
- ext/pitchfork_http/pitchfork_http.c
|
94
84
|
- ext/pitchfork_http/pitchfork_http.rl
|
95
85
|
- ext/pitchfork_http/pitchfork_http_common.rl
|
96
86
|
- lib/pitchfork.rb
|
97
|
-
- lib/pitchfork/app/old_rails/static.rb
|
98
87
|
- lib/pitchfork/children.rb
|
99
88
|
- lib/pitchfork/chunked.rb
|
100
89
|
- lib/pitchfork/configurator.rb
|
@@ -138,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
127
|
- !ruby/object:Gem::Version
|
139
128
|
version: '0'
|
140
129
|
requirements: []
|
141
|
-
rubygems_version: 3.5.
|
130
|
+
rubygems_version: 3.5.9
|
142
131
|
signing_key:
|
143
132
|
specification_version: 4
|
144
133
|
summary: Rack HTTP server for fast clients and Unix
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# -*- encoding: binary -*-
|
2
|
-
# frozen_string_literal: true
|
3
|
-
# :enddoc:
|
4
|
-
# This code is based on the original Rails handler in Mongrel
|
5
|
-
# Copyright (c) 2005 Zed A. Shaw
|
6
|
-
# Copyright (c) 2009 Eric Wong
|
7
|
-
# You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
|
8
|
-
# the GPLv3
|
9
|
-
|
10
|
-
# Static file handler for Rails < 2.3. This handler is only provided
|
11
|
-
# as a convenience for developers. Performance-minded deployments should
|
12
|
-
# use nginx (or similar) for serving static files.
|
13
|
-
#
|
14
|
-
# This supports page caching directly and will try to resolve a
|
15
|
-
# request in the following order:
|
16
|
-
#
|
17
|
-
# * If the requested exact PATH_INFO exists as a file then serve it.
|
18
|
-
# * If it exists at PATH_INFO+rest_operator+".html" exists
|
19
|
-
# then serve that.
|
20
|
-
#
|
21
|
-
# This means that if you are using page caching it will actually work
|
22
|
-
# with Pitchfork and you should see a decent speed boost (but not as
|
23
|
-
# fast as if you use a static server like nginx).
|
24
|
-
class Pitchfork::App::OldRails::Static < Struct.new(:app, :root, :file_server)
|
25
|
-
FILE_METHODS = { 'GET' => true, 'HEAD' => true }
|
26
|
-
|
27
|
-
# avoid allocating new strings for hash lookups
|
28
|
-
REQUEST_METHOD = 'REQUEST_METHOD'
|
29
|
-
REQUEST_URI = 'REQUEST_URI'
|
30
|
-
PATH_INFO = 'PATH_INFO'
|
31
|
-
|
32
|
-
def initialize(app)
|
33
|
-
self.app = app
|
34
|
-
self.root = "#{::RAILS_ROOT}/public"
|
35
|
-
self.file_server = ::Rack::File.new(root)
|
36
|
-
end
|
37
|
-
|
38
|
-
def call(env)
|
39
|
-
# short circuit this ASAP if serving non-file methods
|
40
|
-
FILE_METHODS.include?(env[REQUEST_METHOD]) or return app.call(env)
|
41
|
-
|
42
|
-
# first try the path as-is
|
43
|
-
path_info = env[PATH_INFO].chomp("/")
|
44
|
-
if File.file?("#{root}/#{::Rack::Utils.unescape(path_info)}")
|
45
|
-
# File exists as-is so serve it up
|
46
|
-
env[PATH_INFO] = path_info
|
47
|
-
return file_server.call(env)
|
48
|
-
end
|
49
|
-
|
50
|
-
# then try the cached version:
|
51
|
-
path_info << ActionController::Base.page_cache_extension
|
52
|
-
|
53
|
-
if File.file?("#{root}/#{::Rack::Utils.unescape(path_info)}")
|
54
|
-
env[PATH_INFO] = path_info
|
55
|
-
return file_server.call(env)
|
56
|
-
end
|
57
|
-
|
58
|
-
app.call(env) # call OldRails
|
59
|
-
end
|
60
|
-
end if defined?(Pitchfork::App::OldRails)
|