pitchfork 0.11.0 → 0.11.1
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 +1 -1
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +6 -7
- data/docs/DESIGN.md +1 -1
- data/docs/FORK_SAFETY.md +8 -6
- data/docs/PHILOSOPHY.md +2 -2
- data/docs/REFORKING.md +6 -6
- data/docs/SIGNALS.md +3 -3
- data/ext/pitchfork_http/epollexclusive.h +15 -7
- data/ext/pitchfork_http/extconf.rb +2 -0
- data/lib/pitchfork/http_parser.rb +6 -1
- data/lib/pitchfork/http_server.rb +2 -1
- data/lib/pitchfork/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5dc129b2e6e6e940be0f9e388400a376614a65a125a64b68c73b919744c433b3
|
4
|
+
data.tar.gz: b12e1d5f360edc7159567060c095b40510e8e89c54dae058882742c29c830f78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a43b0204dc0e77a4d28007dbfd89e9645724a4198ea6ec7ffa0895aefaf075bd42d4303c37d0f7e61afb550b665b78ab6b22ca9f934ef2d3721883acf0a68cd
|
7
|
+
data.tar.gz: 7eb41807d6971ac948ee264b1f70fd2e16afcf8f289655073c876b752a53a405b9705e97509f5be82c519ee12694ecf1910b18a20cce8aad3ce9a4ffd3af9ac6
|
data/.github/workflows/ci.yml
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,18 +1,18 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pitchfork (0.11.
|
4
|
+
pitchfork (0.11.1)
|
5
5
|
rack (>= 2.0)
|
6
6
|
raindrops (~> 0.7)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
-
minitest (5.
|
12
|
-
nio4r (2.
|
13
|
-
puma (6.
|
11
|
+
minitest (5.22.2)
|
12
|
+
nio4r (2.7.0)
|
13
|
+
puma (6.4.2)
|
14
14
|
nio4r (~> 2.0)
|
15
|
-
rack (3.0.
|
15
|
+
rack (3.0.9.1)
|
16
16
|
raindrops (0.20.1)
|
17
17
|
rake (13.0.6)
|
18
18
|
rake-compiler (1.2.1)
|
@@ -20,8 +20,7 @@ GEM
|
|
20
20
|
|
21
21
|
PLATFORMS
|
22
22
|
aarch64-linux
|
23
|
-
arm64-darwin
|
24
|
-
arm64-darwin-22
|
23
|
+
arm64-darwin
|
25
24
|
x86_64-linux
|
26
25
|
|
27
26
|
DEPENDENCIES
|
data/docs/DESIGN.md
CHANGED
@@ -49,7 +49,7 @@
|
|
49
49
|
nothing to accept().
|
50
50
|
|
51
51
|
* Since non-blocking accept() is used, there can be a thundering
|
52
|
-
herd when an occasional client connects when application
|
52
|
+
herd when an occasional client connects when the application
|
53
53
|
*is not busy*. The thundering herd problem should not affect
|
54
54
|
applications that are running all the time since worker processes
|
55
55
|
will only select()/accept() outside of the application dispatch.
|
data/docs/FORK_SAFETY.md
CHANGED
@@ -3,11 +3,11 @@
|
|
3
3
|
Because `pitchfork` is a preforking server, your application code and libraries
|
4
4
|
must be fork safe.
|
5
5
|
|
6
|
-
Generally code might be fork-unsafe for one of two reasons
|
6
|
+
Generally, code might be fork-unsafe for one of two reasons.
|
7
7
|
|
8
8
|
## Inherited Connection
|
9
9
|
|
10
|
-
When a process is forked, any open file
|
10
|
+
When a process is forked, any open file descriptors (sockets, files, pipes, etc)
|
11
11
|
end up shared between the parent and child process. This is never what you
|
12
12
|
want, so any code keeping persistent connections should close them either
|
13
13
|
before or after the fork happens.
|
@@ -39,7 +39,7 @@ The documentation of any database client or network library you use should be
|
|
39
39
|
read with care to figure out how to disconnect it, and whether it is best to
|
40
40
|
do it before or after fork.
|
41
41
|
|
42
|
-
Since the most common Ruby application servers `Puma`, `Unicorn` and `Passenger`
|
42
|
+
Since the most common Ruby application servers like `Puma`, `Unicorn` and `Passenger`
|
43
43
|
have forking at least as an option, the requirements are generally well documented.
|
44
44
|
|
45
45
|
However what is novel with `Pitchfork`, is that processes can be forked more than once.
|
@@ -61,7 +61,7 @@ So any libraries that spawn a background thread for periodical work may need to
|
|
61
61
|
that a fork happened and that it should restart its thread.
|
62
62
|
|
63
63
|
Just like with connections, some libraries take on them to automatically restart their background
|
64
|
-
thread when they detect a fork happened.
|
64
|
+
thread when they detect that a fork happened.
|
65
65
|
|
66
66
|
# Refork Safety
|
67
67
|
|
@@ -71,7 +71,7 @@ but not work in Pitchfork when reforking is enabled.
|
|
71
71
|
This is because it is not uncommon for network connections or background threads to only be
|
72
72
|
initialized upon the first request. As such they're not inherited on the first fork.
|
73
73
|
|
74
|
-
However when reforking is enabled, new processes
|
74
|
+
However when reforking is enabled, new processes are forked out of a warmed up process, as such
|
75
75
|
any lazily created connection is much more likely to have been created.
|
76
76
|
|
77
77
|
As such, if you enable reforking for the first time, it is heavily recommended to first do it
|
@@ -88,4 +88,6 @@ impact of discovering such bug.
|
|
88
88
|
- The `ruby-vips` gem binds the `libvips` image processing library that isn't fork safe.
|
89
89
|
(https://github.com/libvips/libvips/discussions/3577)
|
90
90
|
|
91
|
-
|
91
|
+
- Any gem binding with `libgobject`, such as the `gda` gem, likely aren't fork safe.
|
92
|
+
|
93
|
+
No other gem is known to be incompatible for now, but if you find one, please open an issue to add it to the list.
|
data/docs/PHILOSOPHY.md
CHANGED
@@ -80,8 +80,8 @@ Suitable options include `nginx`, `caddy` and likely several others.
|
|
80
80
|
One of the main advantages of threaded servers over preforking servers is their
|
81
81
|
lower memory usage.
|
82
82
|
|
83
|
-
However `pitchfork` solves this with its reforking feature. If enabled and properly configured
|
84
|
-
it very significantly increase Copy-on-Write performance, closing the gap with threaded servers.
|
83
|
+
However `pitchfork` solves this with its reforking feature. If enabled and properly configured,
|
84
|
+
it can very significantly increase Copy-on-Write performance, closing the gap with threaded servers.
|
85
85
|
|
86
86
|
## Assume Modern Deployment Methods
|
87
87
|
|
data/docs/REFORKING.md
CHANGED
@@ -21,7 +21,7 @@ forked processes are essentially free.
|
|
21
21
|
So in theory, preforking servers shouldn't use more memory than threaded servers.
|
22
22
|
|
23
23
|
However, in a Ruby process, there is generally a lot of memory regions that are lazily initialized.
|
24
|
-
This
|
24
|
+
This includes the Ruby Virtual Machine inline caches, JITed code if you use YJIT, and also
|
25
25
|
some common patterns in applications, such as memoization:
|
26
26
|
|
27
27
|
```ruby
|
@@ -35,13 +35,13 @@ end
|
|
35
35
|
However, since workers are forked right after boot, most codepaths have never been executed,
|
36
36
|
so most of these caches are not yet initialized.
|
37
37
|
|
38
|
-
As more code
|
39
|
-
of shared memory of a
|
38
|
+
As more code gets executed, more and more memory pages get invalidated. If you were to graph the ratio
|
39
|
+
of shared memory of a Ruby process over time, you'd likely see a logarithmic curve, with a quick degradation
|
40
40
|
during the first few processed request as the most common code paths get warmed up, and then a stabilization.
|
41
41
|
|
42
42
|
### Reforking
|
43
43
|
|
44
|
-
That is where reforking helps. Since
|
44
|
+
That is where reforking helps. Since most of these invalidations only happen when a code path is executed for the
|
45
45
|
first time, if you take a warmed up worker out of rotation, and use it to fork new workers, warmed up pages will
|
46
46
|
be shared again, and most of them won't be invalidated anymore.
|
47
47
|
|
@@ -123,9 +123,9 @@ PID COMMAND
|
|
123
123
|
```
|
124
124
|
|
125
125
|
However the `pitchfork` master process registers itself as a "child subreaper" via [`PR_SET_CHILD_SUBREAPER`](https://man7.org/linux/man-pages/man2/prctl.2.html).
|
126
|
-
This means any descendant process that is orphaned will be re-parented as a child of the master rather than a child of the init process (pid 1).
|
126
|
+
This means that any descendant process that is orphaned will be re-parented as a child of the master process rather than a child of the init process (pid 1).
|
127
127
|
|
128
|
-
With this in mind, the mold forks twice to create an orphaned process that will get re-attached to the master,
|
128
|
+
With this in mind, the mold forks twice to create an orphaned process that will get re-attached to the master process,
|
129
129
|
effectively forking a sibling rather than a child. Similarly, workers do the same when forking new molds.
|
130
130
|
This technique eases killing previous generations of molds and workers.
|
131
131
|
|
data/docs/SIGNALS.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
## Signal handling
|
2
2
|
|
3
|
-
In general, signals need only be sent to the master process. However,
|
3
|
+
In general, signals need to only be sent to the master process. However,
|
4
4
|
the signals Pitchfork uses internally to communicate with the worker
|
5
5
|
processes are documented here as well.
|
6
6
|
|
@@ -22,8 +22,8 @@ processes are documented here as well.
|
|
22
22
|
### Worker Processes
|
23
23
|
|
24
24
|
Note: the master uses a pipe to signal workers
|
25
|
-
instead of `kill(2)` for most cases. Using signals still
|
26
|
-
remains supported for external tools/libraries
|
25
|
+
instead of `kill(2)` for most cases. Using signals still works and
|
26
|
+
remains supported for external tools/libraries, however.
|
27
27
|
|
28
28
|
Sending signals directly to the worker processes should not normally be
|
29
29
|
needed. If the master process is running, any exited worker will be
|
@@ -21,6 +21,16 @@
|
|
21
21
|
# define USE_EPOLL (0)
|
22
22
|
#endif
|
23
23
|
|
24
|
+
#ifndef HAVE_RB_IO_DESCRIPTOR /* Ruby < 3.1 */
|
25
|
+
static int rb_io_descriptor(VALUE io)
|
26
|
+
{
|
27
|
+
rb_io_t *fptr;
|
28
|
+
GetOpenFile(io, fptr);
|
29
|
+
rb_io_check_closed(fptr);
|
30
|
+
return fptr->fd;
|
31
|
+
}
|
32
|
+
#endif
|
33
|
+
|
24
34
|
#if USE_EPOLL
|
25
35
|
/*
|
26
36
|
* :nodoc:
|
@@ -54,8 +64,7 @@ static VALUE prep_readers(VALUE cls, VALUE readers)
|
|
54
64
|
*/
|
55
65
|
e.events = EPOLLEXCLUSIVE | EPOLLIN;
|
56
66
|
io = rb_io_get_io(io);
|
57
|
-
|
58
|
-
rc = epoll_ctl(epfd, EPOLL_CTL_ADD, fptr->fd, &e);
|
67
|
+
rc = epoll_ctl(epfd, EPOLL_CTL_ADD, rb_io_descriptor(io), &e);
|
59
68
|
if (rc < 0) rb_sys_fail("epoll_ctl");
|
60
69
|
}
|
61
70
|
return epio;
|
@@ -65,7 +74,7 @@ static VALUE prep_readers(VALUE cls, VALUE readers)
|
|
65
74
|
#if USE_EPOLL
|
66
75
|
struct ep_wait {
|
67
76
|
struct epoll_event event;
|
68
|
-
|
77
|
+
VALUE io;
|
69
78
|
int timeout_msec;
|
70
79
|
};
|
71
80
|
|
@@ -79,7 +88,7 @@ static void *do_wait(void *ptr) /* runs w/o GVL */
|
|
79
88
|
* at-a-time (c.f. fs/eventpoll.c in linux.git, it's quite
|
80
89
|
* easy-to-understand for anybody familiar with Ruby C).
|
81
90
|
*/
|
82
|
-
return (void *)(long)epoll_wait(epw->
|
91
|
+
return (void *)(long)epoll_wait(rb_io_descriptor(epw->io), &epw->event, 1,
|
83
92
|
epw->timeout_msec);
|
84
93
|
}
|
85
94
|
|
@@ -93,11 +102,10 @@ get_readers(VALUE epio, VALUE ready, VALUE readers, VALUE timeout_msec)
|
|
93
102
|
|
94
103
|
Check_Type(ready, T_ARRAY);
|
95
104
|
Check_Type(readers, T_ARRAY);
|
96
|
-
|
97
|
-
GetOpenFile(epio, epw.fptr);
|
98
|
-
|
105
|
+
epw.io = rb_io_get_io(epio);
|
99
106
|
epw.timeout_msec = NUM2INT(timeout_msec);
|
100
107
|
n = (long)rb_thread_call_without_gvl(do_wait, &epw, RUBY_UBF_IO, NULL);
|
108
|
+
RB_GC_GUARD(epw.io);
|
101
109
|
if (n < 0) {
|
102
110
|
if (errno != EINTR) rb_sys_fail("epoll_wait");
|
103
111
|
} else if (n > 0) { /* maxevents is hardcoded to 1 */
|
@@ -3,6 +3,8 @@ require 'mkmf'
|
|
3
3
|
|
4
4
|
have_const("PR_SET_CHILD_SUBREAPER", "sys/prctl.h")
|
5
5
|
have_func("rb_enc_interned_str", "ruby.h") # Ruby 3.0+
|
6
|
+
have_func("rb_io_descriptor", "ruby.h") # Ruby 3.1+
|
7
|
+
|
6
8
|
if RUBY_VERSION.start_with?('3.0.')
|
7
9
|
# https://bugs.ruby-lang.org/issues/18772
|
8
10
|
$CFLAGS << ' -DRB_ENC_INTERNED_STR_NULL_CHECK=1 '
|
@@ -193,7 +193,12 @@ module Pitchfork
|
|
193
193
|
|
194
194
|
# called by ext/pitchfork_http/pitchfork_http.rl via rb_funcall
|
195
195
|
def self.is_chunked?(v) # :nodoc:
|
196
|
-
vals = v.split(
|
196
|
+
vals = v.split(',')
|
197
|
+
vals.each do |val|
|
198
|
+
val.strip!
|
199
|
+
val.downcase!
|
200
|
+
end
|
201
|
+
|
197
202
|
if vals.pop == 'chunked'.freeze
|
198
203
|
return true unless vals.include?('chunked'.freeze)
|
199
204
|
raise Pitchfork::HttpParserError, 'double chunked', []
|
@@ -722,6 +722,7 @@ module Pitchfork
|
|
722
722
|
|
723
723
|
proc_name status: "requests: #{worker.requests_count}, processing: #{env["PATH_INFO"]}"
|
724
724
|
|
725
|
+
env["pitchfork.worker"] = worker
|
725
726
|
timeout_handler.rack_env = env
|
726
727
|
env["pitchfork.timeout"] = timeout_handler
|
727
728
|
|
@@ -1049,7 +1050,7 @@ module Pitchfork
|
|
1049
1050
|
exit
|
1050
1051
|
end
|
1051
1052
|
else
|
1052
|
-
clean_fork(&block)
|
1053
|
+
Pitchfork.clean_fork(&block)
|
1053
1054
|
end
|
1054
1055
|
end
|
1055
1056
|
|
data/lib/pitchfork/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pitchfork
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.11.
|
4
|
+
version: 0.11.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jean Boussier
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: raindrops
|
@@ -138,7 +138,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
138
138
|
- !ruby/object:Gem::Version
|
139
139
|
version: '0'
|
140
140
|
requirements: []
|
141
|
-
rubygems_version: 3.
|
141
|
+
rubygems_version: 3.5.5
|
142
142
|
signing_key:
|
143
143
|
specification_version: 4
|
144
144
|
summary: Rack HTTP server for fast clients and Unix
|