Skip to content

Commit 2f5056b

Browse files
Improve server cleanup with graceful shutdown and timeout handling
Co-authored-by: me <[email protected]>
1 parent 2005c6b commit 2f5056b

File tree

1 file changed

+61
-11
lines changed

1 file changed

+61
-11
lines changed

exercises/02.start/01.solution/test/globalSetup.ts

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ export default async function setup(project: TestProject) {
121121
async function startServers() {
122122
console.log('Starting servers...')
123123

124+
// Start app server if necessary
125+
await startAppServerIfNecessary()
126+
124127
// Start the MCP server from the exercise directory
125128
console.log(`Starting MCP server on port ${mcpServerPort}...`)
126129
mcpServerProcess = execa(
@@ -166,21 +169,68 @@ export default async function setup(project: TestProject) {
166169
async function cleanup() {
167170
console.log('Cleaning up servers...')
168171

172+
const cleanupPromises: Array<Promise<void>> = []
173+
169174
if (mcpServerProcess && !mcpServerProcess.killed) {
170-
mcpServerProcess.kill('SIGTERM')
171-
try {
172-
await mcpServerProcess
173-
} catch {
174-
// Process was killed, which is expected
175-
}
175+
cleanupPromises.push(
176+
(async () => {
177+
mcpServerProcess.kill('SIGTERM')
178+
// Give it 2 seconds to gracefully shutdown, then force kill
179+
const timeout = setTimeout(() => {
180+
if (!mcpServerProcess.killed) {
181+
mcpServerProcess.kill('SIGKILL')
182+
}
183+
}, 2000)
184+
185+
try {
186+
await mcpServerProcess
187+
} catch {
188+
// Process was killed, which is expected
189+
} finally {
190+
clearTimeout(timeout)
191+
}
192+
})()
193+
)
176194
}
177195

178196
if (appServerProcess && !appServerProcess.killed) {
179-
appServerProcess.kill('SIGTERM')
180-
try {
181-
await appServerProcess
182-
} catch {
183-
// Process was killed, which is expected
197+
cleanupPromises.push(
198+
(async () => {
199+
appServerProcess.kill('SIGTERM')
200+
// Give it 2 seconds to gracefully shutdown, then force kill
201+
const timeout = setTimeout(() => {
202+
if (!appServerProcess.killed) {
203+
appServerProcess.kill('SIGKILL')
204+
}
205+
}, 2000)
206+
207+
try {
208+
await appServerProcess
209+
} catch {
210+
// Process was killed, which is expected
211+
} finally {
212+
clearTimeout(timeout)
213+
}
214+
})()
215+
)
216+
}
217+
218+
// Wait for all cleanup to complete, but with an overall timeout
219+
try {
220+
await Promise.race([
221+
Promise.all(cleanupPromises),
222+
new Promise((_, reject) =>
223+
setTimeout(() => reject(new Error('Cleanup timeout')), 5000)
224+
)
225+
])
226+
} catch (error) {
227+
console.warn('Cleanup warning:', error.message)
228+
// Force kill any remaining processes
229+
if (mcpServerProcess && !mcpServerProcess.killed) {
230+
mcpServerProcess.kill('SIGKILL')
231+
}
232+
if (appServerProcess && !appServerProcess.killed) {
233+
appServerProcess.kill('SIGKILL')
184234
}
185235
}
186236

0 commit comments

Comments
 (0)