pitchfork 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 before_fork/after_fork hooks.
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, Message::PromoteWorker
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 acknowlege_promotion(control_socket)
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
- @requests_drop[@drop_offset] = 0
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
- @requests_drop.incr(@drop_offset)
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
- @requests_drop = REQUEST_DROPS[drop_index] ||= Raindrops.new(PER_DROP)
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
- when "deployment"
83
- middleware.delete(:ShowExceptions)
84
- middleware.delete(:Lint)
62
+ Rack::Builder.new do
63
+ use(Rack::Lint)
64
+ run inner_app
65
+ end.to_app
85
66
  else
86
- return inner_app
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 mold_selector configurator tmpio http_response worker http_server
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.1.2
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-01-30 00:00:00.000000000 Z
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.3.7
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