Skip to content

Commit 4de8da3

Browse files
Fix multiple stops during child_exit hook.
1 parent 48cfdd8 commit 4de8da3

4 files changed

Lines changed: 17 additions & 13 deletions

File tree

async-service.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ Gem::Specification.new do |spec|
2727
spec.required_ruby_version = ">= 3.2"
2828

2929
spec.add_dependency "async"
30-
spec.add_dependency "async-container", "~> 0.33"
30+
spec.add_dependency "async-container", "~> 0.34"
3131
spec.add_dependency "string-format", "~> 0.2"
3232
end

lib/async/service/policy.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ def child_exit(container, child, status, name:, key:, **options)
5151
rate = container.statistics.failure_rate.per_second
5252

5353
if rate > @failure_rate_threshold
54-
# Only stop if container is still running (avoid redundant stop calls during shutdown)
55-
if container.running?
54+
# Only stop if container is not already stopping
55+
unless container.stopping?
5656
Console.error(self, "Failure rate exceeded threshold, stopping container!",
5757
rate: rate,
5858
threshold: @failure_rate_threshold

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Use `container.stopping?` in policy to prevent redundant stop calls during graceful shutdown.
6+
37
## v0.20.0
48

59
- Introduce `Async::Service::Policy` for monitoring service health and implementing failure handling strategies. Default threshold: 6 failures in 60 seconds (0.1 failures/second).

test/async/service/policy.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def initialize
3030
@statistics = Async::Container::Statistics.new(window: 10)
3131
end
3232

33-
def running?
34-
!@stopped
33+
def stopping?
34+
@stopped
3535
end
3636

3737
def stop(graceful)
@@ -60,12 +60,12 @@ def success?
6060
# 6 failures in same second = 0.6/sec which exceeds 5/10sec = 0.5/sec
6161
rate = mock_container.statistics.failure_rate.per_second
6262
expect(rate).to be > 0.5
63-
expect(mock_container.stopped).to be == false
63+
expect(mock_container).not.to be(:stopped)
6464

6565
# Trigger policy check
6666
policy.child_exit(mock_container, nil, mock_status, name: "test", key: nil)
6767

68-
expect(mock_container.stopped).to be == true
68+
expect(mock_container).to be(:stopped)
6969
end
7070

7171
it "does not stop container when failure rate is acceptable" do
@@ -84,7 +84,7 @@ def success?
8484
# Trigger policy check
8585
policy.child_exit(mock_container, nil, mock_status, name: "test", key: nil)
8686

87-
expect(mock_container.stopped).to be == false
87+
expect(mock_container).not.to be(:stopped)
8888
end
8989

9090
it "does nothing on successful exit" do
@@ -96,7 +96,7 @@ def success_status.success?; true; end
9696
# Even with low threshold, success shouldn't trigger stop
9797
policy.child_exit(mock_container, nil, success_status, name: "test", key: nil)
9898

99-
expect(mock_container.stopped).to be == false
99+
expect(mock_container).not.to be(:stopped)
100100
end
101101

102102
it "does not stop container if already stopping" do
@@ -109,14 +109,14 @@ def success_status.success?; true; end
109109

110110
# Manually stop the container first
111111
mock_container.stop(true)
112-
expect(mock_container.stopped).to be == true
113-
expect(mock_container.running?).to be == false
112+
expect(mock_container).to be(:stopped)
113+
expect(mock_container).to be(:stopping?)
114114

115115
# Policy should not call stop again
116116
policy.child_exit(mock_container, nil, mock_status, name: "test", key: nil)
117117

118-
# Container is still stopped (no error from redundant stop call)
119-
expect(mock_container.stopped).to be == true
118+
# Container is still stopped (no redundant stop call)
119+
expect(mock_container).to be(:stopped)
120120
end
121121
end
122122

0 commit comments

Comments
 (0)