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.

@@ -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