From 9f6aaf713a1672bd06cd7a99c7bf22284ad87aea Mon Sep 17 00:00:00 2001 From: Diego Guerra Date: Fri, 4 Oct 2024 11:45:11 +0800 Subject: [PATCH] Deal with server not ready gracefully --- lib/puma/metrics/app.rb | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/puma/metrics/app.rb b/lib/puma/metrics/app.rb index 7e951d0..f9b761f 100644 --- a/lib/puma/metrics/app.rb +++ b/lib/puma/metrics/app.rb @@ -6,6 +6,8 @@ module Puma module Metrics + MetricsNotAvailableError = Class.new(StandardError) + class App def initialize(launcher) @launcher = launcher @@ -20,16 +22,36 @@ def call(_env) { 'Content-Type' => 'text/plain' }, [Prometheus::Client::Formats::Text.marshal(Prometheus::Client.registry)] ] + rescue MetricsNotAvailableError => e + [503, { 'Content-Type' => 'text/plain' }, ["#{e.message}\n"]] end def retrieve_and_parse_stats! - puma_stats = @launcher.stats + puma_stats = fetch_stats if puma_stats.is_a?(Hash) # Modern Puma outputs stats as a Symbol-keyed Hash @parser.parse(puma_stats) else @parser.parse(JSON.parse(puma_stats, symbolize_names: true)) end end + + private + + def fetch_stats + @launcher.stats + rescue NoMethodError + # Puma plugins are started in the background along with the server, so + # there's a chance that a request will arrive to the server started by + # this plugin before the main one has been registered in the launcher. + # + # If that happens, fetching the stats fails because `@server` is nil, + # causing a NoMethodError. + # + # Ideally this code should detect the case using the launcher public + # interface, but no methods expose the information that is required for + # that. + raise MetricsNotAvailableError, 'Puma is booting up. Stats are not yet ready.' + end end end end