Skip to content

fix: make named task/create idempotent under concurrent requests - #478

Open
Yi-111-a wants to merge 1 commit into
scaleapi:mainfrom
Yi-111-a:fix/concurrent-task-create
Open

Yi-111-a wants to merge 1 commit into
scaleapi:mainfrom
Yi-111-a:fix/concurrent-task-create

Conversation

@Yi-111-a

@Yi-111-a Yi-111-a commented Nov 4, 2026 •

Copy link
Copy Markdown

Problem

task/create is get-or-create keyed on the task name, but the lookup and the insert aren't atomic. If two requests with the same new name arrive at the same time (for example a client retry or a double submit), both miss the lookup and both try to insert. The unique constraint on tasks.name rejects the second insert, so that caller gets a 400 Item already exists instead of the existing task the API promises to return.

Fix

When an insert fails on a duplicate task name, assume another request created the task first:

  • Look the task up once more and return it through the normal existing-task path. That path still resumes an INTERRUPTED task and applies the new params, the same as for any existing task.
  • Only the request whose insert succeeded grants the caller access to the new task. The request that lost the race doesn't grant it a second time.
  • Duplicate errors on unnamed tasks aren't affected and are re-raised as before.

Implementation

Modified TaskRepository.create() in agentex/src/domain/repositories/task_repository.py to catch IntegrityError on the unique tasks.name constraint. When a duplicate name is detected, the existing task is looked up and returned, with the losing request's params merged into the winner's task.

RetriggerView in GreptileConfidence Score: 4/5

Fix the failed-session lookup before merging; concurrent named requests still return an error.

Fix All in CursorFindings

  1. P1 Concurrent task creation still fails ▶
Fix with agent prompt
### Issue 1
agentex/src/domain/repositories/task_repository.py:183-185
When two requests insert the same new task name, the losing request's `session.flush()` raises `IntegrityError` and leaves the session unusable until rollback. The next `session.execute()` raises `PendingRollbackError`, so the caller gets HTTP 500 instead of the winning task. Roll back before the lookup, or isolate the insert in a savepoint.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Summary

This PR adds a duplicate-name recovery branch to TaskRepository.create.

  • Concurrent named task requests now return the same task.

Diagram

sequenceDiagram
    participant A as First request
    participant B as Concurrent request
    participant DB as PostgreSQL
    A->>DB: Insert named task and commit
    B->>DB: Insert the same name
    DB-->>B: IntegrityError
    B->>B: Query without rollback
    B-->>B: PendingRollbackError
Loading

Reviews (1) · Last reviewed commit: "fix: make named task/create idempotent u..." · Reviewed by Greptile

When an insert fails on a duplicate task name, assume another request
created the task first. Look up the existing task and return it through
the normal existing-task path, applying the losing request's params to
the winner's task. Duplicate errors on unnamed tasks are re-raised as before.
@Yi-111-a
Yi-111-a requested a review from a team as a code owner November 4, 2026 03:09
Comment on lines +183 to +185
existing = (
await session.execute(select(TaskORM).where(TaskORM.name == task.name))
).scalar_one()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Concurrent task creation still fails

When two requests insert the same new task name, the losing request's session.flush() raises IntegrityError and leaves the session unusable until rollback. The next session.execute() raises PendingRollbackError, so the caller gets HTTP 500 instead of the winning task. Roll back before the lookup, or isolate the insert in a savepoint.

Prompt To Fix With AI
This is a comment left during a code review.
Path: agentex/src/domain/repositories/task_repository.py
Line: 183-185

Comment:
**Concurrent task creation still fails**

When two requests insert the same new task name, the losing request's `session.flush()` raises `IntegrityError` and leaves the session unusable until rollback. The next `session.execute()` raises `PendingRollbackError`, so the caller gets HTTP 500 instead of the winning task. Roll back before the lookup, or isolate the insert in a savepoint.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Cursor Fix in Claude Code Fix in Codex

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant