pitchfork 0.8.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c9d47a7cd3604f0807a41882322b4461edf2a990439e152dfa2d94bb731eff7
4
- data.tar.gz: 1a38267df9cff0493452fa88c65cb4c9a502643aaf05c8e0b60544f93e9f6e4f
3
+ metadata.gz: eb4e22969b9f2c38717f0cfa7a3c966995814156a17589bc06bdc609b7ad6e32
4
+ data.tar.gz: dbf7833c26ef94962abbd71d66c63e40dd0f7f405ee82951723ad3b4cbfa864f
5
5
  SHA512:
6
- metadata.gz: e8318fc2ae118a7e4a89f65e76e0634d306abd1838f0e4957f638bc870ddd202bc72c006fe3b93b6eec52f1765daea43b24b68eb7b8249b5b21b28a8b9abd51b
7
- data.tar.gz: 5f23586cf49e29649496e15577ce7344c477b99878bfd6756f685f2a96ad40fc202d4f3cf97dad8183dd50b2489423c1fcfd48e20303d4eafae6b7f1db586e9b
6
+ metadata.gz: 0f0c029a01bc999d90421fe0ed77f76c6f1e56a9f3ec8eaa4ab6722f40eb0ee7f9aa0f004044e4c97e93a593cdba0d7a6bab01ef6b075440c76012f05e5cccd4
7
+ data.tar.gz: 4ae4d319ffd2acbf99ad29156622510c951ca095c20074f8415376d6e217be1b959e68e3245590cce62de66f3f8af8f0aae469fec842865dd4dcfe1b060e3db9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Unreleased
2
2
 
3
+ # 0.9.0
4
+
5
+ - Implement `spawn_timeout` to protect against bugs causing workers to get stuck before they reach ready state.
6
+
3
7
  # 0.8.0
4
8
 
5
9
  - Add an `after_monitor_ready` callback, called in the monitor process at end of boot.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pitchfork (0.8.0)
4
+ pitchfork (0.9.0)
5
5
  rack (>= 2.0)
6
6
  raindrops (~> 0.7)
7
7
 
data/README.md CHANGED
@@ -9,13 +9,6 @@ advantage of features in Unix/Unix-like kernels. Slow clients should
9
9
  only be served by placing a reverse proxy capable of fully buffering
10
10
  both the request and response in between `pitchfork` and slow clients.
11
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
12
  ## Features
20
13
 
21
14
  * Designed for Rack, Linux, fast clients, and ease-of-debugging. We
@@ -40,9 +33,23 @@ any issue that may arise.
40
33
  or ports yourself. `pitchfork` can spawn and manage any number of
41
34
  worker processes you choose to scale to your backend.
42
35
 
36
+ * Adaptative timeout: request timeout can be extended dynamically on a
37
+ per request basis, which allows to keep a strict overall timeout for
38
+ most endpoints, but allow a few endpoints to take longer.
39
+
43
40
  * Load balancing is done entirely by the operating system kernel.
44
41
  Requests never pile up behind a busy worker process.
45
42
 
43
+ ## When to Use
44
+
45
+ Pitchfork isn't inherently better than other Ruby application servers, it mostly
46
+ focus on different tradeoffs.
47
+
48
+ If you are fine with your current server, it's best to stick with it.
49
+
50
+ If there is a problem you are trying to solve, please read the
51
+ [migration guide](docs/WHY_MIGRATE.md) first.
52
+
46
53
  ## Requirements
47
54
 
48
55
  Ruby(MRI) Version 2.5 and above.
@@ -238,6 +238,19 @@ exit or be SIGKILL-ed due to timeouts.
238
238
  See https://nginx.org/en/docs/http/ngx_http_upstream_module.html
239
239
  for more details on nginx upstream configuration.
240
240
 
241
+ ### `spawn_timeout`
242
+
243
+ ```ruby
244
+ timeout 5
245
+ ```
246
+
247
+ Sets the timeout for a newly spawned worker to be ready after being spawned.
248
+
249
+ This timeout is a safeguard against various low-level fork safety bugs that could cause
250
+ a process to dead-lock.
251
+
252
+ The default of `10` seconds is quite generous and likely doesn't need to be adjusted.
253
+
241
254
  ### `logger`
242
255
 
243
256
  ```ruby
@@ -0,0 +1,93 @@
1
+ # Why migrate to Pitchfork?
2
+
3
+ First and foremost, if you don't have any specific problem with your current server, then don't.
4
+
5
+ Pitchfork isn't a silver bullet, it's a very opinionated software that focus on very specific tradeoffs,
6
+ that are different from other servers.
7
+
8
+ ## Coming from Unicorn
9
+
10
+ ### Why Migrate?
11
+
12
+ #### Adaptative timeout
13
+
14
+ Pitchfork allows to extend the request timeout on a per request basis,
15
+ this can be helpful when trying to reduce the global request timeout
16
+ to a saner value. You can enforce a stricter value, and extend it
17
+ in the minority of offending endpoints.
18
+
19
+ #### Memory Usage - Reforking
20
+
21
+ If you are unsatisfied with Unicorn memory usage, but threaded Puma isn't an option
22
+ for you, then Pitchfork may be an option if you are able to enable reforking.
23
+
24
+ However be warned that making an application fork safe can be non-trivial,
25
+ and mistakes can lead to critical bugs.
26
+
27
+ #### Rack 3
28
+
29
+ As of Unicorn `6.1.0`, Rack 3 isn't yet supported by Unicorn.
30
+
31
+ Pitchfork is compatible with Rack 3.
32
+
33
+ ### Why Not Migrate?
34
+
35
+ #### Reduced Features
36
+
37
+ While Pitchfork started as a fork of Unicorn, many features such as daemonization,
38
+ pid file management, hot reload have been stripped.
39
+
40
+ Pitchfork only kept features that makes sense in a containerized world.
41
+
42
+ ## Coming from Puma
43
+
44
+ Generally speaking, compared to (threaded) Puma, Pitchfork *may* offer better latency and isolation at the expense of throughput.
45
+
46
+ ### Why Migrate?
47
+
48
+ #### Latency
49
+
50
+ If you suspect your application is subject to contention on the GVL or some other in-process shared resources,
51
+ then Pitchfork may offer improved latency.
52
+
53
+ It is however heavily recommended to first confirm this suspicion with profiling
54
+ tools such as [gvltools](https://github.com/Shopify/gvltools).
55
+
56
+ If you application isn't subject to in-process contention, Pitchfork is unlikely to improve latency.
57
+
58
+ #### Out of Band Garbage Collection
59
+
60
+ Another advantage of only processing a single request per process is that
61
+ [it allows to periodically trigger garbage collection when the worker isn't processing any request](https://shopify.engineering/adventures-in-garbage-collection).
62
+
63
+ This can significantly improve tail latency at the expense of throughput.
64
+
65
+ #### Resiliency and Isolation
66
+
67
+ Since Pitchfork workers have their own address space and only process one request at a time
68
+ it makes it much harder for one faulty request to impact another.
69
+
70
+ Even if a bug causes Ruby to crash, only the request that triggered the bug will be impacted.
71
+
72
+ If a bug causes Ruby to hang, the monitor process will SIGKILL the worker and the capacity will be
73
+ reclaimed.
74
+
75
+ This makes Pitchfork more resilient to some classes of bugs.
76
+
77
+ #### Thread Safety
78
+
79
+ Pitchfork doesn't require applications to be thread-safe. That is probably the worst reason
80
+ to migrate though.
81
+
82
+ ### Why Not Migrate?
83
+
84
+ #### Memory Usage
85
+
86
+ Without reforking enabled Pitchfork will without a doubt use more memory than threaded Puma.
87
+
88
+ With reforking enabled, results will vary based on the application profile and the number of Puma threads,
89
+ but should be in the same ballpark, sometimes better, but likely worse, this depends on many variables and
90
+ can't really be predicted.
91
+
92
+ However be warned that [making an application fork safe](FORK_SAFETY.md) can be non-trivial,
93
+ and mistakes can lead to critical bugs.
@@ -33,6 +33,7 @@ module Pitchfork
33
33
  DEFAULTS = {
34
34
  :soft_timeout => 20,
35
35
  :cleanup_timeout => 2,
36
+ :spawn_timeout => 10,
36
37
  :timeout => 22,
37
38
  :logger => default_logger,
38
39
  :worker_processes => 1,
@@ -174,6 +175,10 @@ module Pitchfork
174
175
  set_int(:timeout, soft_timeout + cleanup_timeout, 5)
175
176
  end
176
177
 
178
+ def spawn_timeout(seconds)
179
+ set_int(:spawn_timeout, seconds, 1)
180
+ end
181
+
177
182
  def worker_processes(nr)
178
183
  set_int(:worker_processes, nr, 1)
179
184
  end
@@ -74,7 +74,7 @@ module Pitchfork
74
74
  end
75
75
 
76
76
  # :stopdoc:
77
- attr_accessor :app, :timeout, :soft_timeout, :cleanup_timeout, :worker_processes,
77
+ attr_accessor :app, :timeout, :soft_timeout, :cleanup_timeout, :spawn_timeout, :worker_processes,
78
78
  :after_worker_fork, :after_mold_fork,
79
79
  :listener_opts, :children,
80
80
  :orig_app, :config, :ready_pipe,
@@ -556,6 +556,10 @@ module Pitchfork
556
556
  def spawn_worker(worker, detach:)
557
557
  logger.info("worker=#{worker.nr} gen=#{worker.generation} spawning...")
558
558
 
559
+ # We set the deadline before spawning the child so that if for some
560
+ # reason it gets stuck before reaching the worker loop,
561
+ # the monitor process will kill it.
562
+ worker.update_deadline(@spawn_timeout)
559
563
  Pitchfork.fork_sibling do
560
564
  worker.pid = Process.pid
561
565
 
@@ -6,14 +6,35 @@ module Pitchfork
6
6
  module Info
7
7
  @workers_count = 0
8
8
  @fork_safe = true
9
- @kept_ios = ObjectSpace::WeakMap.new
9
+
10
+ class WeakSet # :nodoc
11
+ def initialize
12
+ @map = ObjectSpace::WeakMap.new
13
+ end
14
+
15
+ if RUBY_VERSION < "2.7"
16
+ def <<(object)
17
+ @map[object] = object
18
+ end
19
+ else
20
+ def <<(object)
21
+ @map[object] = true
22
+ end
23
+ end
24
+
25
+ def each(&block)
26
+ @map.each_key(&block)
27
+ end
28
+ end
29
+
30
+ @kept_ios = WeakSet.new
10
31
 
11
32
  class << self
12
33
  attr_accessor :workers_count
13
34
 
14
35
  def keep_io(io)
15
36
  raise ArgumentError, "#{io.inspect} doesn't respond to :to_io" unless io.respond_to?(:to_io)
16
- @kept_ios[io] = io
37
+ @kept_ios << io
17
38
  io
18
39
  end
19
40
 
@@ -22,9 +43,9 @@ module Pitchfork
22
43
  end
23
44
 
24
45
  def close_all_ios!
25
- ignored_ios = [$stdin, $stdout, $stderr]
46
+ ignored_ios = [$stdin, $stdout, $stderr, STDIN, STDOUT, STDERR].uniq.compact
26
47
 
27
- @kept_ios.each_value do |io_like|
48
+ @kept_ios.each do |io_like|
28
49
  ignored_ios << (io_like.is_a?(IO) ? io_like : io_like.to_io)
29
50
  end
30
51
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Pitchfork
4
- VERSION = "0.8.0"
4
+ VERSION = "0.9.0"
5
5
  module Const
6
6
  UNICORN_VERSION = '6.1.0'
7
7
  end
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.8.0
4
+ version: 0.9.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: 2023-09-08 00:00:00.000000000 Z
11
+ date: 2023-09-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: raindrops
@@ -72,6 +72,7 @@ files:
72
72
  - docs/REFORKING.md
73
73
  - docs/SIGNALS.md
74
74
  - docs/TUNING.md
75
+ - docs/WHY_MIGRATE.md
75
76
  - examples/constant_caches.ru
76
77
  - examples/echo.ru
77
78
  - examples/hello.ru