pitchfork 0.14.0 → 0.15.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 +16 -4
- data/.gitignore +1 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +3 -0
- data/docs/CONFIGURATION.md +32 -0
- data/docs/FORK_SAFETY.md +2 -1
- data/ext/pitchfork_http/common_field_optimization.h +8 -5
- data/ext/pitchfork_http/extconf.rb +3 -0
- data/ext/pitchfork_http/pitchfork_http.c +292 -468
- data/ext/pitchfork_http/pitchfork_http.rl +32 -24
- data/lib/pitchfork/chunked.rb +6 -3
- data/lib/pitchfork/configurator.rb +1 -1
- data/lib/pitchfork/http_parser.rb +0 -1
- data/lib/pitchfork/http_response.rb +3 -1
- data/lib/pitchfork/http_server.rb +40 -20
- data/lib/pitchfork/listeners.rb +65 -0
- data/lib/pitchfork/socket_helper.rb +12 -2
- data/lib/pitchfork/version.rb +1 -1
- data/lib/pitchfork/worker.rb +0 -5
- data/pitchfork.gemspec +2 -1
- metadata +18 -4
- data/Gemfile.lock +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a36b331190b5554261852d4a7da0f2e074723cdd1d7ad33a2274a0a532f7ea46
|
4
|
+
data.tar.gz: bb8ade8d07f6669d1f1e1d55500951d7a27057bf03aa7043caa55dfd8813728b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3ef8b8ef5217c5b4cc6a63313151ef2d5c1cb823e25caa99f851def3a24e2ad62527db30c06102acea7cdf0638f9627668b931135a093d31ea99fe30c5ae141c
|
7
|
+
data.tar.gz: 5b92506d1725596b4fb772dd4d0ebe850daf574787d94573083bf0dc3e3e245d5cbde07c7bc7fb85ceb936e33c01c9a71b44aa25c8752b7c4ca325af80ab1957
|
data/.github/workflows/ci.yml
CHANGED
@@ -4,7 +4,7 @@ on: [push, pull_request]
|
|
4
4
|
|
5
5
|
jobs:
|
6
6
|
ruby:
|
7
|
-
name: Ruby ${{ matrix.ruby }} ${{ matrix.rubyopt }}
|
7
|
+
name: Ruby ${{ matrix.ruby }} / Rack ${{ matrix.rack }} ${{ matrix.rubyopt }}
|
8
8
|
timeout-minutes: 15
|
9
9
|
strategy:
|
10
10
|
fail-fast: false
|
@@ -12,15 +12,24 @@ jobs:
|
|
12
12
|
os: ["ubuntu-latest"]
|
13
13
|
ruby: ["ruby-head", "3.3", "3.2", "3.1", "3.0", "2.7", "2.6"]
|
14
14
|
rubyopt: [""]
|
15
|
+
rack: ["~> 3.1"]
|
15
16
|
include:
|
16
17
|
- ruby: "3.3"
|
17
18
|
rubyopt: "--enable-frozen-string-literal"
|
19
|
+
rack: "~> 3.1"
|
20
|
+
- ruby: "3.3"
|
21
|
+
rack: "~> 3.0.0"
|
22
|
+
- ruby: "3.3"
|
23
|
+
rack: "~> 2.0"
|
24
|
+
env:
|
25
|
+
RACK_VERSION: "${{ matrix.rack }}"
|
26
|
+
RUBYOPT: "${{ matrix.rubyopt }}"
|
18
27
|
runs-on: ubuntu-latest
|
19
28
|
steps:
|
20
29
|
- name: Check out code
|
21
30
|
uses: actions/checkout@v4
|
22
31
|
|
23
|
-
- name: Set up Ruby
|
32
|
+
- name: Set up Ruby ${{ matrix.ruby }}
|
24
33
|
uses: ruby/setup-ruby@v1
|
25
34
|
with:
|
26
35
|
ruby-version: ${{ matrix.ruby }}
|
@@ -29,5 +38,8 @@ jobs:
|
|
29
38
|
- name: Install packages
|
30
39
|
run: sudo apt-get install -y ragel socat netcat
|
31
40
|
|
32
|
-
- name: Tests ${{ matrix.rubyopt }}
|
33
|
-
run:
|
41
|
+
- name: Tests Rack ${{ matrix.rack }} ${{ matrix.rubyopt }}
|
42
|
+
run: bundle exec rake
|
43
|
+
|
44
|
+
- name: Ensure ragel output is up-to-date
|
45
|
+
run: git diff --exit-code
|
data/.gitignore
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Unreleased
|
2
2
|
|
3
|
+
# 0.15.0
|
4
|
+
|
5
|
+
- Encode pure ASCII strings in Rack env as UTF-8, as allowed by the rack spec.
|
6
|
+
Other strings remain as ASCII-8BIT aka BINARY as required by the rack spec.
|
7
|
+
- Fix compatibility with Rack 3.1.
|
8
|
+
- Fix `rack.hijack` support.
|
9
|
+
- Support Rack 3 streaming bodies.
|
10
|
+
- Implement listen queues for fairer load balancing (optional).
|
11
|
+
- Assume C99 compatible compiler.
|
12
|
+
- Declare dependency on `logger` for Ruby 3.5 compatibility.
|
13
|
+
|
3
14
|
# 0.14.0
|
4
15
|
|
5
16
|
- Remove the dependency on `raindrops`.
|
data/Gemfile
CHANGED
data/docs/CONFIGURATION.md
CHANGED
@@ -60,6 +60,10 @@ The following options may be specified (but are generally not needed):
|
|
60
60
|
|
61
61
|
Default: `1024`
|
62
62
|
|
63
|
+
Note: if the `queue` option is used, each queue gets an equal share of the
|
64
|
+
total `backlog`. e.g. `backlog: 1028, queues: 8` create `8` sockets with a
|
65
|
+
backlog of `128`.
|
66
|
+
|
63
67
|
Note: with the Linux kernel, the net.core.somaxconn sysctl defaults
|
64
68
|
to 128, capping this value to 128. Raising the sysctl allows a
|
65
69
|
larger backlog (which may not be desirable with multiple,
|
@@ -136,6 +140,34 @@ The following options may be specified (but are generally not needed):
|
|
136
140
|
|
137
141
|
Default: `false` (unset)
|
138
142
|
|
143
|
+
- `queues: Integer`
|
144
|
+
|
145
|
+
Create multiple server sockets (using `reuseport`) and split them
|
146
|
+
between workers to ensure fairer load balancing.
|
147
|
+
|
148
|
+
Linux's `epoll+accept` queue is fundamentally LIFO (see a good writeup at
|
149
|
+
https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/).
|
150
|
+
|
151
|
+
Because of this, the workers with the lowest PID will accept
|
152
|
+
disproportionally more requests than workers with higher PID.
|
153
|
+
It generally isn't a problem, especially when reforking is enabled, but for
|
154
|
+
applications that are routinely over provisioned, it may be desirable to
|
155
|
+
ensure all workers at least get some incoming requests so the can warm up.
|
156
|
+
|
157
|
+
Creating more than one queue allow to restrict which worker can process
|
158
|
+
a given incomming request, hence making the load balancing fairer.
|
159
|
+
|
160
|
+
However it is to be used with care, because if the queueing is made too
|
161
|
+
granular, this may cause pockets of request queueing.
|
162
|
+
|
163
|
+
Default: `1` (unset)
|
164
|
+
|
165
|
+
- `queues_per_worker: Integer`
|
166
|
+
|
167
|
+
Controls how many queues each worker is assigned.
|
168
|
+
|
169
|
+
Default: `queues - 1`.
|
170
|
+
|
139
171
|
- `umask: mode`
|
140
172
|
|
141
173
|
Sets the file mode creation mask for UNIX sockets.
|
data/docs/FORK_SAFETY.md
CHANGED
@@ -83,7 +83,8 @@ impact of discovering such bug.
|
|
83
83
|
- The `grpc` isn't fork safe by default, but starting from version `1.57.0`, it does provide an experimental
|
84
84
|
fork safe option that requires setting an environment variable before loading the library, and calling
|
85
85
|
`GRPC.prefork`, `GRPC.postfork_parent` and `GRPC.postfork_child` around fork calls.
|
86
|
-
(https://github.com/grpc/grpc/pull/33430)
|
86
|
+
(https://github.com/grpc/grpc/pull/33430).
|
87
|
+
You can also use the [`grpc_fork_safety`](https://github.com/Shopify/grpc_fork_safety) gem to make it easier.
|
87
88
|
|
88
89
|
- The `ruby-vips` gem binds the `libvips` image processing library that isn't fork safe.
|
89
90
|
(https://github.com/libvips/libvips/discussions/3577)
|
@@ -61,19 +61,20 @@ static struct common_field common_http_fields[] = {
|
|
61
61
|
#define HTTP_PREFIX_LEN (sizeof(HTTP_PREFIX) - 1)
|
62
62
|
static ID id_uminus;
|
63
63
|
|
64
|
+
/* This helper is used to create rack env keys, they should be UTF-8 */
|
64
65
|
#ifdef HAVE_RB_ENC_INTERNED_STR
|
65
66
|
static VALUE str_new_dd_freeze(const char *ptr, long len)
|
66
67
|
{
|
67
68
|
if (RB_ENC_INTERNED_STR_NULL_CHECK && len == 0) {
|
68
|
-
return rb_enc_interned_str("", len,
|
69
|
+
return rb_enc_interned_str("", len, rb_utf8_encoding());
|
69
70
|
} else {
|
70
|
-
return rb_enc_interned_str(ptr, len,
|
71
|
+
return rb_enc_interned_str(ptr, len, rb_utf8_encoding());
|
71
72
|
}
|
72
73
|
}
|
73
74
|
#else
|
74
75
|
static VALUE str_new_dd_freeze(const char *ptr, long len)
|
75
76
|
{
|
76
|
-
VALUE str =
|
77
|
+
VALUE str = rb_utf8_str_new(ptr, len);
|
77
78
|
return rb_funcall(str, id_uminus, 0);
|
78
79
|
}
|
79
80
|
#endif
|
@@ -119,12 +120,14 @@ static VALUE find_common_field(const char *field, size_t flen)
|
|
119
120
|
*/
|
120
121
|
static VALUE uncommon_field(const char *field, size_t flen)
|
121
122
|
{
|
122
|
-
VALUE f =
|
123
|
+
VALUE f = rb_utf8_str_new(NULL, HTTP_PREFIX_LEN + flen);
|
123
124
|
memcpy(RSTRING_PTR(f), HTTP_PREFIX, HTTP_PREFIX_LEN);
|
124
125
|
memcpy(RSTRING_PTR(f) + HTTP_PREFIX_LEN, field, flen);
|
125
126
|
assert(*(RSTRING_PTR(f) + RSTRING_LEN(f)) == '\0' &&
|
126
127
|
"string didn't end with \\0"); /* paranoia */
|
127
|
-
|
128
|
+
// We freeze the value because it will be used as a hash key,
|
129
|
+
// so if we don't Hash#[]= will dup it.
|
130
|
+
return rb_str_freeze(f);
|
128
131
|
}
|
129
132
|
|
130
133
|
#endif /* common_field_optimization_h */
|
@@ -3,9 +3,12 @@
|
|
3
3
|
require 'mkmf'
|
4
4
|
|
5
5
|
append_cflags("-fvisibility=hidden")
|
6
|
+
append_cflags("-std=c99")
|
7
|
+
|
6
8
|
have_const("PR_SET_CHILD_SUBREAPER", "sys/prctl.h")
|
7
9
|
have_func("rb_enc_interned_str", "ruby.h") # Ruby 3.0+
|
8
10
|
have_func("rb_io_descriptor", "ruby.h") # Ruby 3.1+
|
11
|
+
have_func("rb_hash_new_capa", "ruby.h") # Ruby 3.2+
|
9
12
|
have_func("getpagesize", "unistd.h")
|
10
13
|
|
11
14
|
if RUBY_VERSION.start_with?('3.0.')
|