I reproduced the final disk-backed retry batch disappearing if the process stops after dequeue and before processing finishes.
Basically, DiskQueue.get() unlinks queue_file as soon as msg_count reaches zero. At that point Retry.after_accept() has only handed a download retry to the work phase, or Retry.after_work() has only handed a post retry to the posting phase. Neither operation has completed yet.
Reproduction
I tested fork development at 24de015ccbdd20419ff4cc58e560478009d6ed20 with the real Retry callbacks, disk queues, synthetic messages and temporary local state. Current upstream 446a826bc3c30e6523775809dd85d1544b583c67 has the same unlink path.
I committed two retry messages, retrieved a batch through the callback, stopped before its outcome callback, recreated Retry, and checked the recovered queue.
| Case |
Dequeued before stop |
Recovered after restart |
| No dequeue control |
0/2 |
2/2 |
| Partial dequeue control |
1/2 |
2/2 |
Final batch through Retry.after_accept() |
2/2 |
0/2 |
Final batch through Retry.after_work() |
2/2 |
0/2 |
The controls rule out queue creation, housekeeping, serialization and normal close/reopen. The failure occurs only when DiskQueue.get() returns the final available batch and removes its file.
The download path can lose the retry before transfer completes. The post path can lose it before publication completes. Retry messages have already had ack_id removed, so a restart cannot recover these obligations from the original broker delivery.
I plan to keep the disk queue state until the corresponding retry outcome has been recorded, then remove it at an explicit completion boundary. I will cover download retry, post retry, successful completion and requeue-on-failure behavior in the regression tests.
Upstream #1746 is related to retry expiry but does not cover this dequeue/completion window. I did not find an existing fork issue for it.
I reproduced the final disk-backed retry batch disappearing if the process stops after dequeue and before processing finishes.
Basically,
DiskQueue.get()unlinksqueue_fileas soon asmsg_countreaches zero. At that pointRetry.after_accept()has only handed a download retry to the work phase, orRetry.after_work()has only handed a post retry to the posting phase. Neither operation has completed yet.Reproduction
I tested fork
developmentat24de015ccbdd20419ff4cc58e560478009d6ed20with the realRetrycallbacks, disk queues, synthetic messages and temporary local state. Current upstream446a826bc3c30e6523775809dd85d1544b583c67has the same unlink path.I committed two retry messages, retrieved a batch through the callback, stopped before its outcome callback, recreated
Retry, and checked the recovered queue.Retry.after_accept()Retry.after_work()The controls rule out queue creation, housekeeping, serialization and normal close/reopen. The failure occurs only when
DiskQueue.get()returns the final available batch and removes its file.The download path can lose the retry before transfer completes. The post path can lose it before publication completes. Retry messages have already had
ack_idremoved, so a restart cannot recover these obligations from the original broker delivery.I plan to keep the disk queue state until the corresponding retry outcome has been recorded, then remove it at an explicit completion boundary. I will cover download retry, post retry, successful completion and requeue-on-failure behavior in the regression tests.
Upstream #1746 is related to retry expiry but does not cover this dequeue/completion window. I did not find an existing fork issue for it.