pitchfork 0.1.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.
Potentially problematic release.
This version of pitchfork might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/.git-blame-ignore-revs +3 -0
- data/.gitattributes +5 -0
- data/.github/workflows/ci.yml +30 -0
- data/.gitignore +23 -0
- data/COPYING +674 -0
- data/Dockerfile +4 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +67 -0
- data/README.md +123 -0
- data/Rakefile +72 -0
- data/docs/Application_Timeouts.md +74 -0
- data/docs/CONFIGURATION.md +388 -0
- data/docs/DESIGN.md +86 -0
- data/docs/FORK_SAFETY.md +80 -0
- data/docs/PHILOSOPHY.md +90 -0
- data/docs/REFORKING.md +113 -0
- data/docs/SIGNALS.md +38 -0
- data/docs/TUNING.md +106 -0
- data/examples/constant_caches.ru +43 -0
- data/examples/echo.ru +25 -0
- data/examples/hello.ru +5 -0
- data/examples/nginx.conf +156 -0
- data/examples/pitchfork.conf.minimal.rb +5 -0
- data/examples/pitchfork.conf.rb +77 -0
- data/examples/unicorn.socket +11 -0
- data/exe/pitchfork +116 -0
- data/ext/pitchfork_http/CFLAGS +13 -0
- data/ext/pitchfork_http/c_util.h +116 -0
- data/ext/pitchfork_http/child_subreaper.h +25 -0
- data/ext/pitchfork_http/common_field_optimization.h +130 -0
- data/ext/pitchfork_http/epollexclusive.h +124 -0
- data/ext/pitchfork_http/ext_help.h +38 -0
- data/ext/pitchfork_http/extconf.rb +14 -0
- data/ext/pitchfork_http/global_variables.h +97 -0
- data/ext/pitchfork_http/httpdate.c +79 -0
- data/ext/pitchfork_http/pitchfork_http.c +4318 -0
- data/ext/pitchfork_http/pitchfork_http.rl +1024 -0
- data/ext/pitchfork_http/pitchfork_http_common.rl +76 -0
- data/lib/pitchfork/app/old_rails/static.rb +59 -0
- data/lib/pitchfork/children.rb +124 -0
- data/lib/pitchfork/configurator.rb +314 -0
- data/lib/pitchfork/const.rb +23 -0
- data/lib/pitchfork/http_parser.rb +206 -0
- data/lib/pitchfork/http_response.rb +63 -0
- data/lib/pitchfork/http_server.rb +822 -0
- data/lib/pitchfork/launcher.rb +9 -0
- data/lib/pitchfork/mem_info.rb +36 -0
- data/lib/pitchfork/message.rb +130 -0
- data/lib/pitchfork/mold_selector.rb +29 -0
- data/lib/pitchfork/preread_input.rb +33 -0
- data/lib/pitchfork/refork_condition.rb +21 -0
- data/lib/pitchfork/select_waiter.rb +9 -0
- data/lib/pitchfork/socket_helper.rb +199 -0
- data/lib/pitchfork/stream_input.rb +152 -0
- data/lib/pitchfork/tee_input.rb +133 -0
- data/lib/pitchfork/tmpio.rb +35 -0
- data/lib/pitchfork/version.rb +8 -0
- data/lib/pitchfork/worker.rb +244 -0
- data/lib/pitchfork.rb +158 -0
- data/pitchfork.gemspec +30 -0
- metadata +137 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
pitchfork (0.1.0)
|
5
|
+
rack (>= 2.0)
|
6
|
+
raindrops (~> 0.7)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
minitest (5.16.3)
|
12
|
+
rack (3.0.0)
|
13
|
+
raindrops (0.20.0)
|
14
|
+
rake (13.0.6)
|
15
|
+
rake-compiler (1.2.0)
|
16
|
+
rake
|
17
|
+
|
18
|
+
PLATFORMS
|
19
|
+
aarch64-linux
|
20
|
+
arm64-darwin-21
|
21
|
+
x86_64-linux
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
minitest
|
25
|
+
pitchfork!
|
26
|
+
rake
|
27
|
+
rake-compiler
|
28
|
+
|
29
|
+
BUNDLED WITH
|
30
|
+
2.3.22
|
data/LICENSE
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
Unicorn is copyrighted free software by all contributors, see logs in
|
2
|
+
revision control for names and email addresses of all of them.
|
3
|
+
|
4
|
+
You can redistribute it and/or modify it under either the terms of the
|
5
|
+
GNU General Public License (GPL) as published by the Free Software
|
6
|
+
Foundation (FSF), either version 2 of the License, or (at your option)
|
7
|
+
any later version. We currently prefer the GPLv3 or later for
|
8
|
+
derivative works, but the GPLv2 is fine.
|
9
|
+
|
10
|
+
The complete texts of the GPLv2 and GPLv3 are below:
|
11
|
+
GPLv2 - https://www.gnu.org/licenses/gpl-2.0.txt
|
12
|
+
GPLv3 - https://www.gnu.org/licenses/gpl-3.0.txt
|
13
|
+
|
14
|
+
You may (against our _preference_) also use the Ruby 1.8 license terms
|
15
|
+
which we inherited from the original Mongrel project when we forked it:
|
16
|
+
|
17
|
+
=== Ruby 1.8-specific terms (if you're not using the GPL)
|
18
|
+
|
19
|
+
1. You may make and give away verbatim copies of the source form of the
|
20
|
+
software without restriction, provided that you duplicate all of the
|
21
|
+
original copyright notices and associated disclaimers.
|
22
|
+
|
23
|
+
2. You may modify your copy of the software in any way, provided that
|
24
|
+
you do at least ONE of the following:
|
25
|
+
|
26
|
+
a) place your modifications in the Public Domain or otherwise make them
|
27
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
28
|
+
equivalent medium, or by allowing the author to include your
|
29
|
+
modifications in the software.
|
30
|
+
|
31
|
+
b) use the modified software only within your corporation or
|
32
|
+
organization.
|
33
|
+
|
34
|
+
c) rename any non-standard executables so the names do not conflict with
|
35
|
+
standard executables, which must also be provided.
|
36
|
+
|
37
|
+
d) make other distribution arrangements with the author.
|
38
|
+
|
39
|
+
3. You may distribute the software in object code or executable
|
40
|
+
form, provided that you do at least ONE of the following:
|
41
|
+
|
42
|
+
a) distribute the executables and library files of the software,
|
43
|
+
together with instructions (in the manual page or equivalent) on where
|
44
|
+
to get the original distribution.
|
45
|
+
|
46
|
+
b) accompany the distribution with the machine-readable source of the
|
47
|
+
software.
|
48
|
+
|
49
|
+
c) give non-standard executables non-standard names, with
|
50
|
+
instructions on where to get the original software distribution.
|
51
|
+
|
52
|
+
d) make other distribution arrangements with the author.
|
53
|
+
|
54
|
+
4. You may modify and include the part of the software into any other
|
55
|
+
software (possibly commercial). But some files in the distribution
|
56
|
+
are not written by the author, so that they are not under this terms.
|
57
|
+
|
58
|
+
5. The scripts and library files supplied as input to or produced as
|
59
|
+
output from the software do not automatically fall under the
|
60
|
+
copyright of the software, but belong to whomever generated them,
|
61
|
+
and may be sold commercially, and may be aggregated with this
|
62
|
+
software.
|
63
|
+
|
64
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
65
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
66
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
67
|
+
PURPOSE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# pitchfork: Rack HTTP server for shared-nothing architecture
|
2
|
+
|
3
|
+
`pitchfork` is a preforking HTTP server for Rack applications designed
|
4
|
+
to minimize memory usage by maximizing Copy-on-Write performance.
|
5
|
+
|
6
|
+
Like [`unicorn`](https://yhbt.net/unicorn/README.html) (which `pitchfork` is a derivative of), is it designed to
|
7
|
+
only serve fast clients on low-latency, high-bandwidth connections and take
|
8
|
+
advantage of features in Unix/Unix-like kernels. Slow clients should
|
9
|
+
only be served by placing a reverse proxy capable of fully buffering
|
10
|
+
both the request and response in between `pitchfork` and slow clients.
|
11
|
+
|
12
|
+
## Disclaimer
|
13
|
+
|
14
|
+
Until this notice is removed from the README, `pitchfork` should be
|
15
|
+
considered experimental. As such it is not encouraged to run it in
|
16
|
+
production just yet unless you feel capable of debugging yourself
|
17
|
+
any issue that may arise.
|
18
|
+
|
19
|
+
## Features
|
20
|
+
|
21
|
+
* Designed for Rack, Linux, fast clients, and ease-of-debugging. We
|
22
|
+
cut out everything that is better supported by the operating system,
|
23
|
+
[nginx](https://nginx.org/) or [Rack](https://rack.github.io/).
|
24
|
+
|
25
|
+
* Shared-Nothing architecture: workers all run within their own isolated
|
26
|
+
address space and only serve one client at a time for maximum performance
|
27
|
+
and robustness. Concurrent requests don't need to compete for the GVL,
|
28
|
+
or impact each others latency when triggering garbage collection.
|
29
|
+
It also does not care if your application is thread-safe or not.
|
30
|
+
|
31
|
+
* Reforking: `pitchfork` can be configured to periodically promote a warmed up worker
|
32
|
+
as the new template from which workers are forked. This dramatically improves
|
33
|
+
the proportion of shared memory, making processes use only marginally more
|
34
|
+
memory than threads would.
|
35
|
+
|
36
|
+
* Compatible with Ruby 2.5.0 and later.
|
37
|
+
|
38
|
+
* Process management: `pitchfork` will reap and restart workers that
|
39
|
+
die from broken apps. There is no need to manage multiple processes
|
40
|
+
or ports yourself. `pitchfork` can spawn and manage any number of
|
41
|
+
worker processes you choose to scale to your backend.
|
42
|
+
|
43
|
+
* Load balancing is done entirely by the operating system kernel.
|
44
|
+
Requests never pile up behind a busy worker process.
|
45
|
+
|
46
|
+
## Requirements
|
47
|
+
|
48
|
+
Ruby(MRI) Version 2.5 and above.
|
49
|
+
|
50
|
+
`pitchfork` can be used on any Unix-like system, however the reforking
|
51
|
+
feature requires `PR_SET_CHILD_SUBREAPER` which is a Linux 3.4 (May 2012) feature.
|
52
|
+
|
53
|
+
## Installation
|
54
|
+
|
55
|
+
Add this line to your application's Gemfile:
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
gem "pitchfork"
|
59
|
+
```
|
60
|
+
|
61
|
+
And then execute:
|
62
|
+
|
63
|
+
```bash
|
64
|
+
$ bundle install
|
65
|
+
```
|
66
|
+
|
67
|
+
Or install it yourself as:
|
68
|
+
|
69
|
+
```
|
70
|
+
$ gem install pitchfork
|
71
|
+
```
|
72
|
+
|
73
|
+
## Usage
|
74
|
+
|
75
|
+
### Rack
|
76
|
+
|
77
|
+
In your application root directory, run:
|
78
|
+
|
79
|
+
```bash
|
80
|
+
$ bundle exec pitchfork
|
81
|
+
```
|
82
|
+
|
83
|
+
`pitchfork` will bind to all interfaces on TCP port 8080 by default.
|
84
|
+
You may use the `--listen` switch to bind to a different
|
85
|
+
address:port or a UNIX socket.
|
86
|
+
|
87
|
+
### Configuration File(s)
|
88
|
+
|
89
|
+
`pitchfork` will look for the config.ru file used by rackup in APP_ROOT.
|
90
|
+
|
91
|
+
For deployments, it can use a config file for pitchfork-specific options
|
92
|
+
specified by the `--config-file/-c` command-line switch.
|
93
|
+
See the [configuration documentation](docs/CONFIGURATION.md) for the syntax
|
94
|
+
of the pitchfork-specific options.
|
95
|
+
|
96
|
+
The default settings are designed for maximum out-of-the-box
|
97
|
+
compatibility with existing applications.
|
98
|
+
|
99
|
+
Most command-line options for other Rack applications (above) are also
|
100
|
+
supported. Run `pitchfork -h` to see command-line options.
|
101
|
+
|
102
|
+
## License
|
103
|
+
|
104
|
+
pitchfork is copyright 2022 Shopify Inc and all contributors.
|
105
|
+
It is based on Unicorn 6.1.0.
|
106
|
+
|
107
|
+
Unicorn is copyright 2009-2018 by all contributors (see logs in git).
|
108
|
+
It is based on Mongrel 1.1.5.
|
109
|
+
Mongrel is copyright 2007 Zed A. Shaw and contributors.
|
110
|
+
|
111
|
+
pitchfork is licensed under (your choice) of the GPLv2 or later
|
112
|
+
(GPLv3+ preferred), or Ruby (1.8)-specific terms.
|
113
|
+
See the included LICENSE file for details.
|
114
|
+
|
115
|
+
## Thanks
|
116
|
+
|
117
|
+
Thanks to Eric Wong and all Unicorn and Mongrel contributors over the years.
|
118
|
+
Pitchfork would have been much harder to implement otherwise.
|
119
|
+
|
120
|
+
Thanks to Will Jordan who implemented Puma's "fork worker" experimental feature
|
121
|
+
which have been a significant inspiration for Pitchfork.
|
122
|
+
|
123
|
+
Thanks to Peter Bui for letting us use the `pitchfork` name on Rubygems.
|
data/Rakefile
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rake/testtask"
|
4
|
+
require "rake/extensiontask"
|
5
|
+
|
6
|
+
Rake::TestTask.new("test:unit") do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/unit/**/test_*.rb"]
|
10
|
+
t.options = '-v' if ENV['CI'] || ENV['VERBOSE']
|
11
|
+
t.warning = true
|
12
|
+
end
|
13
|
+
|
14
|
+
namespace :test do
|
15
|
+
# It's not so much that these tests are slow, but they tend to fork
|
16
|
+
# and/or register signal handlers, so they if something goes wrong
|
17
|
+
# they are likely to get stuck forever.
|
18
|
+
# The unicorn test suite has historically ran them in individual process
|
19
|
+
# so we continue to do that.
|
20
|
+
task slow: :compile do
|
21
|
+
tests = Dir["test/slow/**/*.rb"].flat_map do |test_file|
|
22
|
+
File.read(test_file).scan(/def (test_\w+)/).map do |test|
|
23
|
+
[test_file] + test
|
24
|
+
end
|
25
|
+
end
|
26
|
+
tests.each do |file, test|
|
27
|
+
sh "ruby", "-Ilib:test", file, "-n", test, "-v"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Unicorn had its own POSIX-shell based testing framework.
|
32
|
+
# It's quite hard to work with and it would be good to convert all this
|
33
|
+
# to Ruby integration tests, but while pitchfork is a moving target it's
|
34
|
+
# preferable to edit the test suite as little as possible.
|
35
|
+
task integration: :compile do
|
36
|
+
File.write("test/integration/random_blob", File.read("/dev/random", 1_000_000))
|
37
|
+
lib = File.expand_path("lib", __dir__)
|
38
|
+
path = "#{File.expand_path("exe", __dir__)}:#{ENV["PATH"]}"
|
39
|
+
old_path = ENV["PATH"]
|
40
|
+
ENV["PATH"] = "#{path}:#{old_path}"
|
41
|
+
begin
|
42
|
+
Dir.chdir("test/integration") do
|
43
|
+
Dir["t[0-9]*.sh"].each do |integration_test|
|
44
|
+
sh("rm", "-rf", "trash")
|
45
|
+
sh("mkdir", "trash")
|
46
|
+
command = ["sh", integration_test]
|
47
|
+
command << "-v" if ENV["VERBOSE"] || ENV["CI"]
|
48
|
+
sh(*command)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
ensure
|
52
|
+
ENV["PATH"] = old_path
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
Rake::ExtensionTask.new("pitchfork_http") do |ext|
|
58
|
+
ext.ext_dir = 'ext/pitchfork_http'
|
59
|
+
ext.lib_dir = 'lib/pitchfork'
|
60
|
+
end
|
61
|
+
|
62
|
+
task :ragel do
|
63
|
+
Dir.chdir(File.expand_path("ext/pitchfork_http", __dir__)) do
|
64
|
+
puts "* compiling pitchfork_http.rl"
|
65
|
+
cmd = ["ragel", "-G2", "pitchfork_http.rl", "-o", "pitchfork_http.c"]
|
66
|
+
system(*cmd) or raise "== #{cmd.join(' ')} failed =="
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
task test: %i(test:unit test:slow test:integration)
|
71
|
+
|
72
|
+
task default: %i(ragel compile test)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Application Timeouts
|
2
|
+
|
3
|
+
This article focuses on _application_ setup for Rack applications, but
|
4
|
+
can be expanded to all applications that connect to external resources
|
5
|
+
and expect short response times.
|
6
|
+
|
7
|
+
This article is not specific to `pitchfork`, but exists to discourage
|
8
|
+
the overuse of the built-in `timeout` directive in `pitchfork`.
|
9
|
+
|
10
|
+
## ALL External Resources Are Considered Unreliable
|
11
|
+
|
12
|
+
Network reliability can _never_ be guaranteed. Network failures cannot
|
13
|
+
be detected reliably by the client (Rack application) in a reasonable
|
14
|
+
timeframe, not even on a LAN.
|
15
|
+
|
16
|
+
Thus, application authors must configure timeouts when interacting with
|
17
|
+
external resources.
|
18
|
+
|
19
|
+
Most database adapters allow configurable timeouts.
|
20
|
+
|
21
|
+
`Net::HTTP` and `Net::SMTP` in the Ruby standard library allow
|
22
|
+
configurable timeouts.
|
23
|
+
|
24
|
+
Even for things as fast as [memcached](https://memcached.org/),
|
25
|
+
[dalli](https://rubygems.org/gems/dalli) and other memcached clients,
|
26
|
+
all offer configurable timeouts.
|
27
|
+
|
28
|
+
Consult the relevant documentation for the libraries you use on
|
29
|
+
how to configure these timeouts.
|
30
|
+
|
31
|
+
## Timeout module in the Ruby standard library
|
32
|
+
|
33
|
+
Ruby offers a Timeout module in its standard library. It has several
|
34
|
+
caveats and is not always reliable:
|
35
|
+
|
36
|
+
* /Some/ Ruby C extensions are not interrupted/timed-out gracefully by
|
37
|
+
this module (report these bugs to extension authors, please) but
|
38
|
+
pure-Ruby components should be.
|
39
|
+
|
40
|
+
* `Timeout` uses [`Thread#raise` which most code don't and probably can't
|
41
|
+
handle properly](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/).
|
42
|
+
A process in which a `Timeout.timeout` block expired
|
43
|
+
should be considered corrupted and should exit as soon as possible.
|
44
|
+
|
45
|
+
* Long-running tasks may run inside `ensure' clauses after timeout
|
46
|
+
fires, causing the timeout to be ineffective.
|
47
|
+
|
48
|
+
The Timeout module is a second-to-last-resort solution, timeouts using
|
49
|
+
`IO.select` (or similar) are more reliable. If you depend on libraries
|
50
|
+
that do not offer timeouts when connecting to external resources, kindly
|
51
|
+
ask those library authors to provide configurable timeouts.
|
52
|
+
|
53
|
+
### A Note About Filesystems
|
54
|
+
|
55
|
+
Most operations to regular files on POSIX filesystems are NOT
|
56
|
+
interruptible. Thus, the "timeout" module in the Ruby standard library
|
57
|
+
can not reliably timeout systems with massive amounts of iowait.
|
58
|
+
|
59
|
+
If your app relies on the filesystem, ensure all the data your
|
60
|
+
application works with is small enough to fit in the kernel page cache.
|
61
|
+
Otherwise increase the amount of physical memory you have to match, or
|
62
|
+
employ a fast, low-latency storage system (solid state).
|
63
|
+
|
64
|
+
Volumes mounted over NFS (and thus a potentially unreliable network)
|
65
|
+
must be mounted with timeouts and applications must be prepared to
|
66
|
+
handle network/server failures.
|
67
|
+
|
68
|
+
## The Last Line Of Defense
|
69
|
+
|
70
|
+
The `timeout` mechanism in pitchfork is an extreme solution that should
|
71
|
+
be avoided whenever possible.
|
72
|
+
It will help preserve the platform if your application or a dependency
|
73
|
+
has a bug that cause it to either get stuck or two slow, but it is not a
|
74
|
+
solution to such bugs, merely a mitigation.
|