fix(import-admin): retry-failed + clear-stuck — same UPDATE…WHERE pattern as the maintenance sweep, so neither endpoint can hit psycopg's 65535-parameter ceiling once accumulated row counts exceed ~65k
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,17 +103,22 @@ async def list_tasks():
|
|||||||
|
|
||||||
@import_admin_bp.route("/retry-failed", methods=["POST"])
|
@import_admin_bp.route("/retry-failed", methods=["POST"])
|
||||||
async def retry_failed():
|
async def retry_failed():
|
||||||
|
# Fold SELECT into UPDATE…WHERE…RETURNING — the prior SELECT-then-
|
||||||
|
# UPDATE-WHERE-id-IN pattern blew past psycopg's 65535-parameter
|
||||||
|
# ceiling once failed_ids exceeded ~65k rows.
|
||||||
async with get_session() as session:
|
async with get_session() as session:
|
||||||
failed_ids = (
|
result = await session.execute(
|
||||||
await session.execute(select(ImportTask.id).where(ImportTask.status == "failed"))
|
update(ImportTask)
|
||||||
).scalars().all()
|
.where(ImportTask.status == "failed")
|
||||||
|
.values(
|
||||||
|
status="queued", error=None,
|
||||||
|
started_at=None, finished_at=None,
|
||||||
|
)
|
||||||
|
.returning(ImportTask.id)
|
||||||
|
)
|
||||||
|
failed_ids = [row[0] for row in result.all()]
|
||||||
if not failed_ids:
|
if not failed_ids:
|
||||||
return jsonify({"retried": 0})
|
return jsonify({"retried": 0})
|
||||||
await session.execute(
|
|
||||||
update(ImportTask)
|
|
||||||
.where(ImportTask.id.in_(failed_ids))
|
|
||||||
.values(status="queued", error=None, started_at=None, finished_at=None)
|
|
||||||
)
|
|
||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
from ..tasks.import_file import import_media_file
|
from ..tasks.import_file import import_media_file
|
||||||
@@ -138,28 +143,26 @@ async def clear_stuck():
|
|||||||
autoretry-looped for 2 days after a corrupt-data PIL OSError.
|
autoretry-looped for 2 days after a corrupt-data PIL OSError.
|
||||||
"""
|
"""
|
||||||
async with get_session() as session:
|
async with get_session() as session:
|
||||||
stuck_ids = (
|
# Fold SELECT into UPDATE…WHERE — see /retry-failed for the
|
||||||
await session.execute(
|
# 65535-parameter ceiling rationale. rowcount is enough here
|
||||||
select(ImportTask.id).where(
|
# because we don't need the ids afterward (no .delay()).
|
||||||
ImportTask.status.in_(["pending", "queued", "processing"])
|
clear_result = await session.execute(
|
||||||
)
|
update(ImportTask)
|
||||||
|
.where(
|
||||||
|
ImportTask.status.in_(["pending", "queued", "processing"])
|
||||||
)
|
)
|
||||||
).scalars().all()
|
.values(
|
||||||
if stuck_ids:
|
status="failed",
|
||||||
await session.execute(
|
finished_at=datetime.now(UTC),
|
||||||
update(ImportTask)
|
error=(
|
||||||
.where(ImportTask.id.in_(stuck_ids))
|
"manually cleared via /api/import/clear-stuck "
|
||||||
.values(
|
"— stuck in non-terminal state; retry once "
|
||||||
status="failed",
|
"underlying cause (corrupt file, missing model, "
|
||||||
finished_at=datetime.now(UTC),
|
"etc.) is resolved"
|
||||||
error=(
|
),
|
||||||
"manually cleared via /api/import/clear-stuck "
|
|
||||||
"— stuck in non-terminal state; retry once "
|
|
||||||
"underlying cause (corrupt file, missing model, "
|
|
||||||
"etc.) is resolved"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
tasks_failed = clear_result.rowcount or 0
|
||||||
|
|
||||||
# Finalize any 'running' ImportBatch that no longer has any
|
# Finalize any 'running' ImportBatch that no longer has any
|
||||||
# active children. The "Scanning..." banner is driven by
|
# active children. The "Scanning..." banner is driven by
|
||||||
@@ -195,7 +198,7 @@ async def clear_stuck():
|
|||||||
await session.commit()
|
await session.commit()
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
"tasks_failed": len(stuck_ids),
|
"tasks_failed": tasks_failed,
|
||||||
"batches_finalized": finalized_batches,
|
"batches_finalized": finalized_batches,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user