pitchfork 0.1.2 → 0.2.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 +4 -4
- data/.github/workflows/ci.yml +1 -1
- data/CHANGELOG.md +7 -0
- data/Dockerfile +1 -1
- data/Gemfile.lock +7 -6
- data/Rakefile +10 -2
- data/docs/CONFIGURATION.md +30 -32
- data/examples/pitchfork.conf.rb +2 -2
- data/exe/pitchfork +1 -8
- data/ext/pitchfork_http/epollexclusive.h +13 -17
- data/ext/pitchfork_http/pitchfork_http.c +196 -192
- data/ext/pitchfork_http/pitchfork_http.rl +23 -19
- data/lib/pitchfork/configurator.rb +28 -36
- data/lib/pitchfork/flock.rb +51 -0
- data/lib/pitchfork/http_server.rb +87 -70
- data/lib/pitchfork/refork_condition.rb +3 -3
- data/lib/pitchfork/version.rb +1 -1
- data/lib/pitchfork/worker.rb +21 -16
- data/lib/pitchfork.rb +25 -31
- metadata +4 -4
- data/lib/pitchfork/mold_selector.rb +0 -29
data/lib/pitchfork/worker.rb
CHANGED
@@ -7,14 +7,13 @@ module Pitchfork
|
|
7
7
|
# releases of pitchfork. Knowledge of this class is generally not
|
8
8
|
# not needed for most users of pitchfork.
|
9
9
|
#
|
10
|
-
# Some users may want to access it in the
|
10
|
+
# Some users may want to access it in the after_promotion/after_fork hooks.
|
11
11
|
# See the Pitchfork::Configurator RDoc for examples.
|
12
12
|
class Worker
|
13
13
|
# :stopdoc:
|
14
14
|
EXIT_SIGNALS = [:QUIT, :TERM]
|
15
|
-
@generation = 0
|
16
15
|
attr_accessor :nr, :pid, :generation
|
17
|
-
attr_reader :master
|
16
|
+
attr_reader :master, :requests_count
|
18
17
|
|
19
18
|
def initialize(nr, pid: nil, generation: 0)
|
20
19
|
@nr = nr
|
@@ -23,6 +22,7 @@ module Pitchfork
|
|
23
22
|
@mold = false
|
24
23
|
@to_io = @master = nil
|
25
24
|
@exiting = false
|
25
|
+
@requests_count = 0
|
26
26
|
if nr
|
27
27
|
build_raindrops(nr)
|
28
28
|
else
|
@@ -42,13 +42,17 @@ module Pitchfork
|
|
42
42
|
@exiting
|
43
43
|
end
|
44
44
|
|
45
|
+
def outdated?
|
46
|
+
CURRENT_GENERATION_DROP[0] > @generation
|
47
|
+
end
|
48
|
+
|
45
49
|
def update(message)
|
46
50
|
message.class.members.each do |member|
|
47
51
|
send("#{member}=", message.public_send(member))
|
48
52
|
end
|
49
53
|
|
50
54
|
case message
|
51
|
-
when Message::WorkerPromoted
|
55
|
+
when Message::WorkerPromoted
|
52
56
|
promoted!
|
53
57
|
end
|
54
58
|
end
|
@@ -60,9 +64,10 @@ module Pitchfork
|
|
60
64
|
@master.close
|
61
65
|
end
|
62
66
|
|
63
|
-
def
|
67
|
+
def declare_promotion(control_socket)
|
64
68
|
message = Message::WorkerPromoted.new(@nr, Process.pid, generation)
|
65
69
|
control_socket.sendmsg(message)
|
70
|
+
CURRENT_GENERATION_DROP[0] = @generation
|
66
71
|
end
|
67
72
|
|
68
73
|
def promote(generation)
|
@@ -73,11 +78,17 @@ module Pitchfork
|
|
73
78
|
send_message_nonblock(Message::SpawnWorker.new(new_worker.nr))
|
74
79
|
end
|
75
80
|
|
81
|
+
def promote!
|
82
|
+
@generation += 1
|
83
|
+
promoted!
|
84
|
+
end
|
85
|
+
|
76
86
|
def promoted!
|
77
87
|
@mold = true
|
78
88
|
@nr = nil
|
79
89
|
@drop_offset = 0
|
80
90
|
@tick_drop = MOLD_DROP
|
91
|
+
self
|
81
92
|
end
|
82
93
|
|
83
94
|
def mold?
|
@@ -169,15 +180,11 @@ module Pitchfork
|
|
169
180
|
end
|
170
181
|
|
171
182
|
def reset
|
172
|
-
@
|
173
|
-
end
|
174
|
-
|
175
|
-
def requests_count
|
176
|
-
@requests_drop[@drop_offset]
|
183
|
+
@requests_count = 0
|
177
184
|
end
|
178
185
|
|
179
|
-
def increment_requests_count
|
180
|
-
@
|
186
|
+
def increment_requests_count(by = 1)
|
187
|
+
@requests_count += by
|
181
188
|
end
|
182
189
|
|
183
190
|
# called in both the master (reaping worker) and worker (SIGQUIT handler)
|
@@ -215,9 +222,9 @@ module Pitchfork
|
|
215
222
|
end
|
216
223
|
|
217
224
|
MOLD_DROP = Raindrops.new(1)
|
225
|
+
CURRENT_GENERATION_DROP = Raindrops.new(1)
|
218
226
|
PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE
|
219
227
|
TICK_DROPS = []
|
220
|
-
REQUEST_DROPS = []
|
221
228
|
|
222
229
|
class << self
|
223
230
|
# Since workers are created from another process, we have to
|
@@ -228,7 +235,6 @@ module Pitchfork
|
|
228
235
|
def preallocate_drops(workers_count)
|
229
236
|
0.upto(workers_count / PER_DROP) do |i|
|
230
237
|
TICK_DROPS[i] = Raindrops.new(PER_DROP)
|
231
|
-
REQUEST_DROPS[i] = Raindrops.new(PER_DROP)
|
232
238
|
end
|
233
239
|
end
|
234
240
|
end
|
@@ -237,8 +243,7 @@ module Pitchfork
|
|
237
243
|
drop_index = drop_nr / PER_DROP
|
238
244
|
@drop_offset = drop_nr % PER_DROP
|
239
245
|
@tick_drop = TICK_DROPS[drop_index] ||= Raindrops.new(PER_DROP)
|
240
|
-
@
|
241
|
-
@tick_drop[@drop_offset] = @requests_drop[@drop_offset] = 0
|
246
|
+
@tick_drop[@drop_offset] = 0
|
242
247
|
end
|
243
248
|
end
|
244
249
|
end
|
data/lib/pitchfork.rb
CHANGED
@@ -57,40 +57,15 @@ module Pitchfork
|
|
57
57
|
Object.const_get(File.basename(ru, '.rb').capitalize)
|
58
58
|
end
|
59
59
|
|
60
|
-
if $DEBUG
|
61
|
-
require 'pp'
|
62
|
-
pp({ :inner_app => inner_app })
|
63
|
-
end
|
64
|
-
|
65
|
-
return inner_app unless server.default_middleware
|
66
|
-
|
67
|
-
middleware = { # order matters
|
68
|
-
ContentLength: nil,
|
69
|
-
Chunked: nil,
|
70
|
-
CommonLogger: [ $stderr ],
|
71
|
-
ShowExceptions: nil,
|
72
|
-
Lint: nil,
|
73
|
-
TempfileReaper: nil,
|
74
|
-
}
|
75
|
-
|
76
|
-
# return value, matches rackup defaults based on env
|
77
|
-
# Pitchfork does not support persistent connections, but Rainbows!
|
78
|
-
# and Zbatery both do. Users accustomed to the Rack::Server default
|
79
|
-
# middlewares will need ContentLength/Chunked middlewares.
|
80
60
|
case ENV["RACK_ENV"]
|
81
61
|
when "development"
|
82
|
-
|
83
|
-
|
84
|
-
|
62
|
+
Rack::Builder.new do
|
63
|
+
use(Rack::Lint)
|
64
|
+
run inner_app
|
65
|
+
end.to_app
|
85
66
|
else
|
86
|
-
|
67
|
+
inner_app
|
87
68
|
end
|
88
|
-
Rack::Builder.new do
|
89
|
-
middleware.each do |m, args|
|
90
|
-
use(Rack.const_get(m), *args) if Rack.const_defined?(m)
|
91
|
-
end
|
92
|
-
run inner_app
|
93
|
-
end.to_app
|
94
69
|
end
|
95
70
|
end
|
96
71
|
|
@@ -146,13 +121,32 @@ module Pitchfork
|
|
146
121
|
raise
|
147
122
|
end
|
148
123
|
end
|
124
|
+
|
125
|
+
def self.clean_fork(&block)
|
126
|
+
# We fork from a thread to start with a clean stack.
|
127
|
+
# If we didn't the base stack would grow after each refork
|
128
|
+
# putting an effective limit on the number of generations.
|
129
|
+
parent_thread = Thread.current
|
130
|
+
Thread.new do
|
131
|
+
current_thread = Thread.current
|
132
|
+
# We copy over any thread state it might have
|
133
|
+
parent_thread.keys.each do |key|
|
134
|
+
current_thread[key] = parent_thread[key]
|
135
|
+
end
|
136
|
+
parent_thread.thread_variables.each do |variable|
|
137
|
+
current_thread.thread_variable_set(variable, parent_thread.thread_variable_get(variable))
|
138
|
+
end
|
139
|
+
|
140
|
+
fork(&block)
|
141
|
+
end.value
|
142
|
+
end
|
149
143
|
# :startdoc:
|
150
144
|
end
|
151
145
|
# :enddoc:
|
152
146
|
|
153
147
|
%w(
|
154
148
|
const socket_helper stream_input tee_input mem_info children message http_parser
|
155
|
-
refork_condition
|
149
|
+
refork_condition configurator tmpio http_response worker http_server
|
156
150
|
).each do |s|
|
157
151
|
require_relative "pitchfork/#{s}"
|
158
152
|
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.
|
4
|
+
version: 0.2.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-
|
11
|
+
date: 2023-04-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: raindrops
|
@@ -96,13 +96,13 @@ files:
|
|
96
96
|
- lib/pitchfork/children.rb
|
97
97
|
- lib/pitchfork/configurator.rb
|
98
98
|
- lib/pitchfork/const.rb
|
99
|
+
- lib/pitchfork/flock.rb
|
99
100
|
- lib/pitchfork/http_parser.rb
|
100
101
|
- lib/pitchfork/http_response.rb
|
101
102
|
- lib/pitchfork/http_server.rb
|
102
103
|
- lib/pitchfork/launcher.rb
|
103
104
|
- lib/pitchfork/mem_info.rb
|
104
105
|
- lib/pitchfork/message.rb
|
105
|
-
- lib/pitchfork/mold_selector.rb
|
106
106
|
- lib/pitchfork/preread_input.rb
|
107
107
|
- lib/pitchfork/refork_condition.rb
|
108
108
|
- lib/pitchfork/select_waiter.rb
|
@@ -133,7 +133,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
133
|
- !ruby/object:Gem::Version
|
134
134
|
version: '0'
|
135
135
|
requirements: []
|
136
|
-
rubygems_version: 3.
|
136
|
+
rubygems_version: 3.4.6
|
137
137
|
signing_key:
|
138
138
|
specification_version: 4
|
139
139
|
summary: Rack HTTP server for fast clients and Unix
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Pitchfork
|
4
|
-
module MoldSelector
|
5
|
-
class LeastSharedMemory
|
6
|
-
def call(server)
|
7
|
-
workers = server.children.fresh_workers
|
8
|
-
if workers.empty?
|
9
|
-
server.logger.info("No current generation workers yet")
|
10
|
-
return
|
11
|
-
end
|
12
|
-
candidate = workers.shift
|
13
|
-
|
14
|
-
workers.each do |worker|
|
15
|
-
if worker.meminfo.shared_memory < candidate.meminfo.shared_memory
|
16
|
-
# We suppose that a worker with a lower amount of shared memory
|
17
|
-
# has warmed up more caches & such, hence is closer to stabilize
|
18
|
-
# making it a better candidate.
|
19
|
-
candidate = worker
|
20
|
-
end
|
21
|
-
end
|
22
|
-
parent_meminfo = server.children.mold&.meminfo || MemInfo.new(Process.pid)
|
23
|
-
cow_efficiency = candidate.meminfo.cow_efficiency(parent_meminfo)
|
24
|
-
server.logger.info("worker=#{candidate.nr} pid=#{candidate.pid} selected as new mold shared_memory_kb=#{candidate.meminfo.shared_memory} cow=#{cow_efficiency.round(1)}%")
|
25
|
-
candidate
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|