Context
LiteQueue can retry a locked message, but a claim has no identity beyond message_id. done(), mark_failed(), and retry() update any matching row without checking its current state.
Current code:
pop() changes READY to LOCKED and returns the message.
retry() changes any matching message to READY and leaves its old lock_time in place.
done() and mark_failed() accept acknowledgements from any worker that knows the message ID.
A focused reproduction on commit 2b82ede showed this race:
- Worker A pops a message.
- Recovery retries the message.
- Worker B pops the same message.
- Worker A calls
done(message_id).
- LiteQueue marks worker B's active claim as done.
The stale done() returned True, and the newer claim ended in DONE.
Why implement this
Retry is meant to recover work after a worker stops responding. Without claim ownership, a slow worker can acknowledge a newer delivery. If the newer worker then fails, LiteQueue has already removed the task from active work. This breaks reliable redelivery and can lose work.
Proposed direction
- Add a claim token or monotonically increasing attempt number to each successful pop.
- Include the claim identity in the returned
Message or a dedicated claim type.
- Require
message_id, claim identity, and LOCKED status in done() and mark_failed() updates.
- Restrict retry to documented source states.
- Clear obsolete
lock_time and done_time fields when returning work to READY.
- Provide one atomic operation for retrying expired claims instead of requiring
list_locked() followed by separate retry() calls.
- Document and migrate the schema and acknowledgement API.
Acceptance criteria
Context
LiteQueue can retry a locked message, but a claim has no identity beyond
message_id.done(),mark_failed(), andretry()update any matching row without checking its current state.Current code:
pop()changesREADYtoLOCKEDand returns the message.retry()changes any matching message toREADYand leaves its oldlock_timein place.done()andmark_failed()accept acknowledgements from any worker that knows the message ID.A focused reproduction on commit
2b82edeshowed this race:done(message_id).The stale
done()returnedTrue, and the newer claim ended inDONE.Why implement this
Retry is meant to recover work after a worker stops responding. Without claim ownership, a slow worker can acknowledge a newer delivery. If the newer worker then fails, LiteQueue has already removed the task from active work. This breaks reliable redelivery and can lose work.
Proposed direction
Messageor a dedicated claim type.message_id, claim identity, andLOCKEDstatus indone()andmark_failed()updates.lock_timeanddone_timefields when returning work toREADY.list_locked()followed by separateretry()calls.Acceptance criteria
make testpasses.