Hyeonsang Cho e02d5e2fc3
fix(mcp): skip tasks a cancel released while the poll claim was in flight (#6859)
* fix(mcp): skip tasks a cancel released while the poll claim was in flight

claim_due_tasks selected due rows with FOR UPDATE SKIP LOCKED, then set
the lease through an ORM flush that updates by id alone. FOR UPDATE is a
no-op on SQLite, so a request_cancel that committed between the SELECT
and the flush had its cleared poll lease written back. The cancellation
worker only claims rows without a live lease, so the remote cancel waited
for mcp_tasks.lease_seconds (120 s by default), and the task was polled
once more for a result apply_snapshot then discarded.

The claim now selects candidate ids and leases each one with a
conditional UPDATE that re-checks the claim predicates (pollable status,
no cancel request, due next_poll_at, no live lease), returning only the
rows it actually leased, the same atomic fence release_claim already
uses. On Postgres the row lock still orders the two writers.

* docs(changelog): reference #6859 for the MCP task claim fence

* docs(mcp): note that a short poll claim batch is not topped up
2026-10-15 09:42:46 +08:00
..