Skip to content

Commit cd810da

Browse files
committed
Address PR feedback from Copilot review
- Move deleteRunnerById prepared statement to top with other statements - Fix misleading comment about ON DELETE SET NULL behavior - Add race condition acknowledgment comment with note about orphan detection - Use Promise.allSettled for parallel bulk deletion in frontend - Add user notification when bulk delete has failures - Add comment explaining test runners are simplified without directories
1 parent be73d88 commit cd810da

3 files changed

Lines changed: 29 additions & 11 deletions

File tree

backend/src/routes/pools.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ const updatePoolEnabled = db.prepare(`
8383

8484
const deletePoolById = db.prepare('DELETE FROM runner_pools WHERE id = ?');
8585

86+
const deleteRunnerById = db.prepare('DELETE FROM runners WHERE id = ?');
87+
8688
const getCredentialById = db.prepare('SELECT * FROM credentials WHERE id = ?');
8789

8890
const getPoolRunners = db.prepare(`
@@ -300,9 +302,6 @@ poolsRouter.patch('/:id', async (req: Request, res: Response) => {
300302
}
301303
});
302304

303-
// Prepared statement for deleting a runner from the database
304-
const deleteRunnerById = db.prepare('DELETE FROM runners WHERE id = ?');
305-
306305
/**
307306
* Delete a pool and all its runners
308307
*/
@@ -316,6 +315,9 @@ poolsRouter.delete('/:id', async (req: Request, res: Response) => {
316315
}
317316

318317
// Get all runners belonging to this pool and clean them up
318+
// Note: There's a small race window where a new runner could be added between this query
319+
// and pool deletion. Such runners would have pool_id set to NULL by ON DELETE SET NULL.
320+
// The UI's orphan detection feature can help identify and clean up such cases if they occur.
319321
const poolRunners = getPoolRunners.all(req.params.id) as RunnerRow[];
320322

321323
console.log(`[pools] Deleting pool ${req.params.id} with ${poolRunners.length} runners`);
@@ -348,8 +350,8 @@ poolsRouter.delete('/:id', async (req: Request, res: Response) => {
348350
// Still try to delete the database record
349351
try {
350352
deleteRunnerById.run(runner.id);
351-
} catch {
352-
// Ignore - may already be deleted by ON DELETE SET NULL behavior
353+
} catch (deleteErr) {
354+
console.error(`[pools] Failed to delete runner record ${runner.id} during cleanup:`, deleteErr);
353355
}
354356
}
355357
});

backend/tests/pools.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,9 @@ describe('Pools API', () => {
208208
const poolId = createResponse.body.pool.id;
209209

210210
// Insert test runners directly into the database (simulating runners that exist)
211+
// Note: These are simplified test runners without runner_dir or process_id.
212+
// The cleanup code handles this gracefully - removeRunner will fail but the
213+
// fallback cleanup and database deletion will still succeed.
211214
const runnerId1 = 'test-runner-1-' + Date.now();
212215
const runnerId2 = 'test-runner-2-' + Date.now();
213216

frontend/src/components/RunnerManager.tsx

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -472,13 +472,26 @@ export function RunnerManager() {
472472
return;
473473
}
474474

475-
// Delete runners one by one (could be optimized with a bulk API endpoint)
476-
for (const id of selectedRunnerIds) {
477-
try {
478-
await runnersApi.delete(id);
479-
} catch (err) {
480-
console.error(`Failed to delete runner ${id}:`, err);
475+
// Delete runners in parallel (could be further optimized with a bulk API endpoint)
476+
const ids = Array.from(selectedRunnerIds);
477+
const results = await Promise.allSettled(
478+
ids.map(id => runnersApi.delete(id))
479+
);
480+
481+
// Collect and report failures
482+
const failedIds: string[] = [];
483+
results.forEach((result, index) => {
484+
if (result.status === 'rejected') {
485+
console.error(`Failed to delete runner ${ids[index]}:`, result.reason);
486+
failedIds.push(ids[index]);
481487
}
488+
});
489+
490+
if (failedIds.length > 0) {
491+
alert(
492+
`Failed to delete ${failedIds.length} runner${failedIds.length === 1 ? '' : 's'}. ` +
493+
'Please check the console for more details and try again.'
494+
);
482495
}
483496

484497
setSelectedRunnerIds(new Set());

0 commit comments

Comments
 (0)