upfluence-utils 0.14.0 → 0.14.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e1429c611b80c503096df4c7e19d60866a40aa3042cd1a73d8ec39d76b3a164
4
- data.tar.gz: fcb360825de59c3f0251400597977dacf27ae7c36f76533e750e83cbfd3ae6bb
3
+ metadata.gz: 568c4033f087730c94a75c756587432873ee149b4d2462ea04b1b03169d9b551
4
+ data.tar.gz: e824e37e1cd2c901342f0089e859af8d62a908aa1c755d2527dc1a20309449f2
5
5
  SHA512:
6
- metadata.gz: 45cbf134ed8f525e6b9ca2ac6e5dac7b063d0a69337e83bcff3f424bdcbf08f296aa3006d93f21dd0f4f5a88ada3e586aa01fa568daef4322fa122ff2b9df8c7
7
- data.tar.gz: 6e681f3f08cdd2437c9968305702c325018d7760dcb590f2552247a99150dd56fcb74de5b5aabc74626c904af9a165d0b8567cdf0f711ab0c6c055457626856d
6
+ metadata.gz: 60d4fd0ba8383217d60a73eadf48920d15259c77be69922f7a65f313b9a297dc62274c598655e739f3403a51e0d0f2cdd8f116e714528cc261dfb1a59ea47536
7
+ data.tar.gz: c6cbf56699eb8e3d540dea322fda7920a35c5bb06162d1483aaeb337dae7ea16f1f30137d052aab41497b8596f73efdac21f10a87961060194fdcd00809e1106
@@ -26,6 +26,7 @@ module Upfluence
26
26
  module HTTP
27
27
  class Server
28
28
  REQUEST_CONTEXT_KEY = :uhttp_request_context
29
+ SHUTDOWN_SIGNALS = %w[INT TERM].freeze
29
30
  DEFAULT_MIDDLEWARES = []
30
31
  DEFAULT_OPTIONS = {
31
32
  server: :puma,
@@ -51,7 +52,7 @@ module Upfluence
51
52
  ],
52
53
  admin_port: ENV.fetch('ADMIN_PORT', nil),
53
54
  debug: ENV.fetch('DEBUG', nil)
54
- }.freeze
55
+ }
55
56
 
56
57
  def initialize(options = {}, &block)
57
58
  @options = DEFAULT_OPTIONS.dup.merge(options)
@@ -62,19 +63,26 @@ module Upfluence
62
63
  @admin_builder = admin_builder(opts) if opts[:admin_port]
63
64
  @production_builder = production_builder(opts, &block)
64
65
  @handler = Rack::Handler.get(opts[:server])
66
+ @stoppables = []
65
67
  end
66
68
 
67
69
  def serve
68
70
  ENV['RACK_ENV'] = Upfluence.env.to_s
71
+ @stopping = false
72
+ signal_handlers = install_signal_handlers
73
+ admin_thread = nil
69
74
 
70
- Thread.new { run_prometheus_exporter } if @options[:push_gateway_url]
75
+ register_stoppable(prometheus_exporter_thread) if @options[:push_gateway_url]
71
76
 
72
- @options[:instrumentations].each(&:start)
77
+ @options[:instrumentations].each do |instrumentation|
78
+ instrumentation.start
79
+ register_stoppable(instrumentation) if instrumentation.respond_to?(:stop)
80
+ end
73
81
 
74
82
  if @admin_builder
75
83
  admin_port = @options[:admin_port].to_i
76
84
 
77
- Thread.new do
85
+ admin_thread = Thread.new do
78
86
  run_builder(@admin_builder, Port: admin_port)
79
87
  end
80
88
 
@@ -84,6 +92,10 @@ module Upfluence
84
92
  end
85
93
 
86
94
  run_builder(@production_builder)
95
+ ensure
96
+ stop
97
+ admin_thread&.join
98
+ restore_signal_handlers(signal_handlers) if signal_handlers
87
99
  end
88
100
 
89
101
  class << self
@@ -102,6 +114,9 @@ module Upfluence
102
114
  opts = @options.merge(overrides)
103
115
 
104
116
  @handler.run(builder, **opts) do |server|
117
+ register_stoppable(server)
118
+ register_booted_handler(server)
119
+ configure_puma_instrumenter(server) if builder.equal?(@production_builder)
105
120
  server.threaded = opts[:threaded] if server.respond_to? :threaded=
106
121
 
107
122
  # Thin does not recognize the max_thread argument, howerver it has a
@@ -110,18 +125,62 @@ module Upfluence
110
125
  end
111
126
  end
112
127
 
128
+ def register_booted_handler(server)
129
+ return unless server.respond_to?(:events)
130
+
131
+ server.events.after_booted { install_signal_handlers }
132
+ end
133
+
134
+ def configure_puma_instrumenter(server)
135
+ @options[:instrumentations].select { |instrumentation| instrumentation.respond_to?(:server=) }.each do |instrumentation|
136
+ instrumentation.server = server
137
+ end
138
+ end
139
+
140
+ def install_signal_handlers
141
+ SHUTDOWN_SIGNALS.to_h do |signal|
142
+ previous_handler = Signal.trap(signal) do
143
+ Thread.new { stop }
144
+ end
145
+
146
+ [signal, previous_handler]
147
+ end
148
+ end
149
+
150
+ def restore_signal_handlers(handlers)
151
+ handlers.each { |signal, handler| Signal.trap(signal, handler) }
152
+ end
153
+
154
+ def register_stoppable(stoppable)
155
+ return stoppable.stop if @stopping
156
+
157
+ @stoppables << stoppable
158
+ end
159
+
160
+ def stop
161
+ @stopping = true
162
+ stoppables = @stoppables
163
+ @stoppables = []
164
+
165
+ stoppables.each(&:stop)
166
+ end
167
+
168
+ def prometheus_exporter_thread
169
+ Thread.new { run_prometheus_exporter }.tap do |thread|
170
+ thread.define_singleton_method(:stop) do
171
+ kill
172
+ join
173
+ end
174
+ end
175
+ end
176
+
113
177
  def production_builder(opts, &block)
114
- base_handler = @base_handler
115
178
  has_admin = !opts[:admin_port].nil?
116
- mount = method(:mount_admin_endpoints)
179
+ builder = Builder.new
117
180
 
118
- Builder.new do
119
- use Middleware::RequestStapler, timeout: opts[:request_timeout]
120
- use Middleware::Logger
121
- use Middleware::Prometheus
122
- use Middleware::ApplicationHeaders, base_handler
123
- use Middleware::HandleException
181
+ mount_middlewares(builder, opts)
124
182
 
183
+ builder.instance_eval do
125
184
  if opts[:enable_active_record_middleware] &&
126
185
  defined?(ActiveRecord::Base) &&
127
186
  ActiveRecord::Base.configurations.any?
@@ -130,44 +189,25 @@ module Upfluence
130
189
  use Middleware::ActiveRecord
131
190
  end
132
191
 
133
- use Rack::Timeout, service_timeout: opts[:request_timeout] if opts[:request_timeout]
134
-
135
- use Upfluence.error_logger.middleware
136
-
137
- use Rack::ContentLength
138
- use Rack::Chunked
139
- use Rack::Lint if Upfluence.env.development?
140
- use Rack::TempfileReaper
141
- use Rack::ETag
142
- use Middleware::CORS if Upfluence.env.development?
143
-
144
192
  (DEFAULT_MIDDLEWARES + opts[:middlewares]).each do |m|
145
193
  m = [m] unless m.is_a?(Array)
146
194
  use(*m)
147
195
  end
196
+ end
148
197
 
149
- mount.call(self, opts) unless has_admin
150
-
151
- map '/healthcheck' do
152
- run(opts[:healthcheck_endpoint] || Endpoint::Healthcheck.new)
153
- end
198
+ mount_admin_endpoints(builder, opts) unless has_admin
199
+ builder.instance_eval(&block)
154
200
 
155
- instance_eval(&block)
156
- end
201
+ builder
157
202
  end
158
203
 
159
204
  def admin_builder(opts)
160
- mount = method(:mount_admin_endpoints)
205
+ builder = Builder.new
161
206
 
162
- Builder.new do
163
- use Rack::ContentLength
207
+ mount_middlewares(builder, opts)
208
+ mount_admin_endpoints(builder, opts)
164
209
 
165
- map '/healthcheck' do
166
- run(opts[:healthcheck_endpoint] || Endpoint::Healthcheck.new)
167
- end
168
-
169
- mount.call(self, opts)
170
- end
210
+ builder
171
211
  end
172
212
 
173
213
  def mount_admin_endpoints(builder, opts)
@@ -186,6 +226,35 @@ module Upfluence
186
226
  end
187
227
  end
188
228
 
229
+ def mount_middlewares(builder, opts)
230
+ base_handler = @base_handler
231
+
232
+ builder.instance_eval do
233
+ use Middleware::RequestStapler, timeout: opts[:request_timeout]
234
+ use Middleware::Logger
235
+ use Middleware::Prometheus
236
+ use Middleware::ApplicationHeaders, base_handler
237
+ use Middleware::HandleException
238
+
239
+ use Rack::Timeout, service_timeout: opts[:request_timeout] if opts[:request_timeout]
240
+
241
+ use Upfluence.error_logger.middleware
242
+
243
+ use Rack::ContentLength
244
+ use Rack::Chunked
245
+ use Rack::Lint if Upfluence.env.development?
246
+ use Rack::TempfileReaper
247
+ use Rack::ETag
248
+ use Middleware::CORS if Upfluence.env.development?
249
+ end
250
+
251
+ builder.instance_eval do
252
+ map '/healthcheck' do
253
+ run(opts[:healthcheck_endpoint] || Endpoint::Healthcheck.new)
254
+ end
255
+ end
256
+ end
257
+
189
258
  def run_prometheus_exporter
190
259
  push = Prometheus::Client::Push.new(
191
260
  @options[:app_name],
@@ -30,7 +30,7 @@ module Upfluence
30
30
  rescue => e
31
31
  Upfluence.error_logger.notify(e)
32
32
  ensure
33
- sleep @interval
33
+ sleep @interval unless @stop_thread
34
34
  end
35
35
  end
36
36
  end
@@ -6,6 +6,8 @@ module Upfluence
6
6
  class PumaInstrumenter < PeriodicInstrumenter
7
7
  KEYS = %i[backlog_thread running_thread busy_thread pool_capacity requests_count].freeze
8
8
 
9
+ attr_writer :server
10
+
9
11
  def prefix
10
12
  'puma'
11
13
  end
@@ -17,15 +19,17 @@ module Upfluence
17
19
  end
18
20
 
19
21
  def values
20
- stats = Puma.stats_hash
22
+ return {} unless @server
23
+
24
+ stats = @server.stats
21
25
 
22
26
  {
23
- requests_count: [{ value: stats[:requests_count] }],
24
- backlog_thread: [{ value: stats[:backlog] }],
25
- running_thread: [{ value: stats[:running] }],
26
- busy_thread: [{ value: stats[:busy_threads] }],
27
- pool_capacity: [{ value: stats[:max_threads] }]
28
- }
27
+ requests_count: stats[:requests_count],
28
+ backlog_thread: stats[:backlog],
29
+ running_thread: stats[:running],
30
+ busy_thread: stats[:busy_threads],
31
+ pool_capacity: stats[:max_threads]
32
+ }.compact.transform_values { |value| [{ value: value }] }
29
33
  rescue NoMethodError
30
34
  {}
31
35
  end
@@ -1,5 +1,5 @@
1
1
  module Upfluence
2
2
  module Utils
3
- VERSION = '0.14.0'.freeze
3
+ VERSION = '0.14.1'.freeze
4
4
  end
5
5
  end
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_runtime_dependency 'sentry-ruby', '< 6.0'
27
27
  spec.add_runtime_dependency 'sinatra-contrib'
28
28
  spec.add_runtime_dependency 'activesupport'
29
- spec.add_runtime_dependency 'puma'
29
+ spec.add_runtime_dependency 'puma', '>= 7.0'
30
30
  spec.add_runtime_dependency 'rack', '< 3.0'
31
31
  spec.add_runtime_dependency 'stackprof'
32
32
  spec.add_runtime_dependency 'prometheus-client', '~> 2.1'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: upfluence-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.14.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Upfluence
@@ -141,14 +141,14 @@ dependencies:
141
141
  requirements:
142
142
  - - ">="
143
143
  - !ruby/object:Gem::Version
144
- version: '0'
144
+ version: '7.0'
145
145
  type: :runtime
146
146
  prerelease: false
147
147
  version_requirements: !ruby/object:Gem::Requirement
148
148
  requirements:
149
149
  - - ">="
150
150
  - !ruby/object:Gem::Version
151
- version: '0'
151
+ version: '7.0'
152
152
  - !ruby/object:Gem::Dependency
153
153
  name: rack
154
154
  requirement: !ruby/object:Gem::Requirement