File tree Expand file tree Collapse file tree 5 files changed +48
-5
lines changed
Expand file tree Collapse file tree 5 files changed +48
-5
lines changed Original file line number Diff line number Diff line change @@ -14,7 +14,11 @@ module Body
1414 # then decrements the metric. Use this so requests_active stays elevated until
1515 # the request is fully finished (including response_finished callbacks).
1616 class RequestFinished < Protocol ::HTTP ::Body ::Wrapper
17- # Wrap a response body with a metric. If the body is nil or empty, decrements immediately.
17+ # Wrap a response body with a metric.
18+ #
19+ # Expects the caller to have incremented `requests_active` beforehand. If the body is
20+ # not wrapped (nil or empty message/body, or an error while assigning the wrapper), the
21+ # `ensure` block decrements once. When wrapped, the metric is decremented in {#close}.
1822 #
1923 # @parameter message [Protocol::HTTP::Response] The response whose body to wrap.
2024 # @parameter metric [Async::Utilization::Metric] The metric to decrement when the body is closed.
@@ -29,6 +33,8 @@ def self.wrap(message, metric)
2933 message
3034 end
3135
36+ # Initialize the wrapper.
37+ #
3238 # @parameter body [Protocol::HTTP::Body::Readable] The body to wrap.
3339 # @parameter metric [Async::Utilization::Metric] The metric to decrement on close.
3440 def initialize ( body , metric )
Original file line number Diff line number Diff line change @@ -69,10 +69,22 @@ def accept(...)
6969 # Body::RequestFinished wrapper runs the decrement after the body closes,
7070 # so response_finished callbacks are counted as active.
7171 def call ( ...)
72+ decrement = false
73+
7274 @requests_total_metric . increment
7375 @requests_active_metric . increment
76+ decrement = true
77+
78+ # Roll back `requests_active` unless the full `super` + wrap path completes. Covers
79+ # `super` raising before `wrap` runs, errors inside `wrap`, and other abnormal exits.
80+ response = Body ::RequestFinished . wrap ( super , @requests_active_metric )
81+ decrement = false
7482
75- return Body ::RequestFinished . wrap ( super , @requests_active_metric )
83+ return response
84+ ensure
85+ if decrement
86+ @requests_active_metric . decrement
87+ end
7688 end
7789
7890 # Generates a human-readable string representing the current statistics.
Original file line number Diff line number Diff line change 11# Releases
22
3+ ## Unreleased
4+
5+ - Decrement ` requests_active ` in ` Falcon::Server#call ` when ` super ` or ` Falcon::Body::RequestFinished.wrap ` raises, so utilization metrics are not leaked on error paths.
6+
37## v0.55.2
48
5- - Remove unnecessary require for ` async/service/supervisor/supervised ` ."
9+ - Remove unnecessary require for ` async/service/supervisor/supervised ` .
610
711## v0.55.1
812
Original file line number Diff line number Diff line change 6969 end
7070
7171 with "nil message" do
72- it "decrements immediately" do
72+ it "decrements immediately and returns nil " do
7373 metric . increment
74- subject . wrap ( nil , metric )
74+ expect ( subject . wrap ( nil , metric ) ) . to be == nil
7575 expect ( metric . value ) . to be == 0
7676 end
7777 end
Original file line number Diff line number Diff line change @@ -148,5 +148,26 @@ def make_server(endpoint)
148148 expect ( utilization_registry . metric ( :requests_active ) . value ) . to be == 0
149149 expect ( utilization_registry . metric ( :requests_total ) . value ) . to be == 1
150150 end
151+
152+ it "restores requests_active when delegate raises" do
153+ boom = Object . new
154+ def boom . call ( _request )
155+ raise RuntimeError , "boom"
156+ end
157+ def boom . close
158+ end
159+
160+ endpoint = Async ::HTTP ::Endpoint . parse ( "http://127.0.0.1:0" , reuse_port : true )
161+ server = ::Falcon ::Server . new (
162+ ::Protocol ::HTTP ::Middleware . new ( boom ) ,
163+ endpoint ,
164+ utilization_registry : utilization_registry
165+ )
166+
167+ request = ::Protocol ::HTTP ::Request [ "GET" , "/" , { } ]
168+ expect { server . call ( request ) } . to raise_exception ( RuntimeError , message : be =~ /boom/ )
169+ expect ( utilization_registry . metric ( :requests_active ) . value ) . to be == 0
170+ expect ( utilization_registry . metric ( :requests_total ) . value ) . to be == 1
171+ end
151172 end
152173end
You can’t perform that action at this time.
0 commit comments