|
10 | 10 |
|
11 | 11 | module Async |
12 | 12 | module Container |
13 | | - # The default timeout for interrupting processes, before escalating to terminating. |
14 | | - INTERRUPT_TIMEOUT = ENV.fetch("ASYNC_CONTAINER_INTERRUPT_TIMEOUT", 10).to_f |
15 | | - |
16 | 13 | # The default timeout for terminating processes, before escalating to killing. |
17 | | - TERMINATE_TIMEOUT = ENV.fetch("ASYNC_CONTAINER_TERMINATE_TIMEOUT", 10).to_f |
| 14 | + GRACEFUL_TIMEOUT = ENV.fetch("ASYNC_CONTAINER_GRACEFUL_TIMEOUT", "true").then do |value| |
| 15 | + case value |
| 16 | + when "true" |
| 17 | + true # Default timeout for graceful termination. |
| 18 | + when "false" |
| 19 | + false # Immediately kill the processes. |
| 20 | + else |
| 21 | + value.to_f |
| 22 | + end |
| 23 | + end |
| 24 | + |
| 25 | + # The default timeout for graceful termination. |
| 26 | + DEFAULT_GRACEFUL_TIMEOUT = 10.0 |
18 | 27 |
|
19 | 28 | # Manages a group of running processes. |
20 | 29 | class Group |
@@ -155,50 +164,37 @@ def kill |
155 | 164 | # Stop all child processes with a multi-phase shutdown sequence. |
156 | 165 | # |
157 | 166 | # A graceful shutdown performs the following sequence: |
158 | | - # 1. Send SIGINT and wait up to `interrupt_timeout` seconds |
159 | | - # 2. Send SIGTERM and wait up to `terminate_timeout` seconds |
160 | | - # 3. Send SIGKILL and wait indefinitely for process cleanup |
| 167 | + # 1. Send SIGINT and wait up to `graceful` seconds if specified. |
| 168 | + # 2. Send SIGKILL and wait indefinitely for process cleanup. |
161 | 169 | # |
162 | | - # If `graceful` is false, skips the SIGINT phase and goes directly to SIGTERM → SIGKILL. |
| 170 | + # If `graceful` is true, default to `DEFAULT_GRACEFUL_TIMEOUT` (10 seconds). |
| 171 | + # If `graceful` is false, skip the SIGINT phase and go directly to SIGKILL. |
163 | 172 | # |
164 | | - # @parameter graceful [Boolean] Whether to send SIGINT first or skip directly to SIGTERM. |
165 | | - # @parameter interrupt_timeout [Numeric | Nil] Time to wait after SIGINT before escalating to SIGTERM. |
166 | | - # @parameter terminate_timeout [Numeric | Nil] Time to wait after SIGTERM before escalating to SIGKILL. |
167 | | - def stop(graceful = true, interrupt_timeout: INTERRUPT_TIMEOUT, terminate_timeout: TERMINATE_TIMEOUT) |
168 | | - case graceful |
169 | | - when true |
170 | | - # Use defaults. |
171 | | - when false |
172 | | - interrupt_timeout = nil |
173 | | - when Numeric |
174 | | - interrupt_timeout = graceful |
175 | | - terminate_timeout = graceful |
176 | | - end |
177 | | - |
178 | | - Console.debug(self, "Stopping all processes...", interrupt_timeout: interrupt_timeout, terminate_timeout: terminate_timeout) |
| 173 | + # @parameter graceful [Boolean | Numeric] Whether to send SIGINT first or skip directly to SIGKILL. |
| 174 | + def stop(graceful = GRACEFUL_TIMEOUT) |
| 175 | + Console.info(self, "Stopping all processes...", graceful: graceful) |
179 | 176 |
|
180 | 177 | # If a timeout is specified, interrupt the children first: |
181 | | - if interrupt_timeout |
182 | | - clock = Async::Clock.start |
183 | | - |
184 | | - # Interrupt the children: |
| 178 | + if graceful |
| 179 | + # Send SIGINT to the children: |
185 | 180 | self.interrupt |
186 | 181 |
|
187 | | - # Wait for the children to exit: |
188 | | - self.wait_for_exit(clock, interrupt_timeout) |
189 | | - end |
190 | | - |
191 | | - if terminate_timeout and self.any? |
192 | | - clock = Async::Clock.start |
| 182 | + if graceful == true |
| 183 | + graceful = DEFAULT_GRACEFUL_TIMEOUT |
| 184 | + end |
193 | 185 |
|
194 | | - # If the children are still running, terminate them: |
195 | | - self.terminate |
| 186 | + clock = Clock.start |
196 | 187 |
|
197 | 188 | # Wait for the children to exit: |
198 | | - self.wait_for_exit(clock, terminate_timeout) |
| 189 | + self.wait_for_exit(clock, graceful) |
199 | 190 | end |
200 | | - |
| 191 | + ensure |
| 192 | + # Do our best to clean up the children: |
201 | 193 | if any? |
| 194 | + if graceful |
| 195 | + Console.warn(self, "Killing processes after graceful shutdown failed...", size: self.size, clock: clock) |
| 196 | + end |
| 197 | + |
202 | 198 | self.kill |
203 | 199 | self.wait |
204 | 200 | end |
|
0 commit comments