Fixes to worktree cleanup (#137)

This commit is contained in:
Solomon
2025-07-11 16:55:42 +01:00
committed by GitHub
parent 2ab7b1e482
commit af972143ab
3 changed files with 53 additions and 39 deletions

View File

@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "\n SELECT ta.id as \"attempt_id!: Uuid\", ta.worktree_path, p.git_repo_path as \"git_repo_path!\"\n FROM task_attempts ta\n LEFT JOIN execution_processes ep ON ta.id = ep.task_attempt_id AND ep.completed_at IS NOT NULL\n JOIN tasks t ON ta.task_id = t.id\n JOIN projects p ON t.project_id = p.id\n WHERE ta.worktree_deleted = FALSE\n -- Exclude attempts with any running processes (in progress)\n AND ta.id NOT IN (\n SELECT DISTINCT ep2.task_attempt_id\n FROM execution_processes ep2\n WHERE ep2.completed_at IS NULL\n )\n GROUP BY ta.id, ta.worktree_path, p.git_repo_path, ta.updated_at\n HAVING datetime('now', '-24 hours') > datetime(\n MAX(\n CASE\n WHEN ep.completed_at IS NOT NULL THEN ep.completed_at\n ELSE ta.updated_at\n END\n )\n )\n ORDER BY MAX(\n CASE\n WHEN ep.completed_at IS NOT NULL THEN ep.completed_at\n ELSE ta.updated_at\n END\n ) ASC\n ",
"describe": {
"columns": [
{
"name": "attempt_id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "worktree_path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "git_repo_path!",
"ordinal": 2,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
true,
true
]
},
"hash": "212828320e8d871ab9d83705a040b23bcf0393dc7252177fc539a74657f578ef"
}

View File

@@ -1,32 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT ta.id as \"attempt_id!: Uuid\", ta.worktree_path, p.git_repo_path as \"git_repo_path!\"\n FROM task_attempts ta\n JOIN execution_processes ep ON ta.id = ep.task_attempt_id\n JOIN tasks t ON ta.task_id = t.id\n JOIN projects p ON t.project_id = p.id\n WHERE ep.completed_at IS NOT NULL\n AND ta.worktree_deleted = FALSE\n GROUP BY ta.id, ta.worktree_path, p.git_repo_path\n HAVING datetime('now', '-1 minutes') > datetime(MAX(ep.completed_at))\n AND ta.id NOT IN (\n SELECT DISTINCT ep2.task_attempt_id\n FROM execution_processes ep2\n WHERE ep2.completed_at IS NULL\n )\n ORDER BY MAX(ep.completed_at) ASC\n ",
"describe": {
"columns": [
{
"name": "attempt_id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "worktree_path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "git_repo_path!",
"ordinal": 2,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
true,
true
]
},
"hash": "deb5c6ffadf9e67460c37d82520da032bc472ff6d0bd658925b312b18c090f1a"
}

View File

@@ -379,7 +379,9 @@ impl TaskAttempt {
.collect())
}
/// Find task attempts that are expired (4+ minutes since last activity) and eligible for worktree cleanup
/// Find task attempts that are expired (24+ hours since last activity) and eligible for worktree cleanup
/// Activity includes: execution completion, task attempt updates (including worktree recreation),
/// and any attempts that are currently in progress
pub async fn find_expired_for_cleanup(
pool: &SqlitePool,
) -> Result<Vec<(Uuid, String, String)>, sqlx::Error> {
@@ -387,19 +389,31 @@ impl TaskAttempt {
r#"
SELECT ta.id as "attempt_id!: Uuid", ta.worktree_path, p.git_repo_path as "git_repo_path!"
FROM task_attempts ta
JOIN execution_processes ep ON ta.id = ep.task_attempt_id
LEFT JOIN execution_processes ep ON ta.id = ep.task_attempt_id AND ep.completed_at IS NOT NULL
JOIN tasks t ON ta.task_id = t.id
JOIN projects p ON t.project_id = p.id
WHERE ep.completed_at IS NOT NULL
AND ta.worktree_deleted = FALSE
GROUP BY ta.id, ta.worktree_path, p.git_repo_path
HAVING datetime('now', '-1 minutes') > datetime(MAX(ep.completed_at))
WHERE ta.worktree_deleted = FALSE
-- Exclude attempts with any running processes (in progress)
AND ta.id NOT IN (
SELECT DISTINCT ep2.task_attempt_id
FROM execution_processes ep2
WHERE ep2.completed_at IS NULL
)
ORDER BY MAX(ep.completed_at) ASC
GROUP BY ta.id, ta.worktree_path, p.git_repo_path, ta.updated_at
HAVING datetime('now', '-24 hours') > datetime(
MAX(
CASE
WHEN ep.completed_at IS NOT NULL THEN ep.completed_at
ELSE ta.updated_at
END
)
)
ORDER BY MAX(
CASE
WHEN ep.completed_at IS NOT NULL THEN ep.completed_at
ELSE ta.updated_at
END
) ASC
"#
)
.fetch_all(pool)