Blind retry
The first write succeeded. Repeating the POST created a second ticket.
HTTP · failure handling · Python
A server saves the ticket. The connection closes before its reply arrives. This local experiment measures what happens next under three different retry policies.
The first write succeeded. Repeating the POST created a second ticket.
The second request recovered the receipt for the original ticket.
The client has no receipt. It stops instead of guessing whether another write is safe.
These counts come from the local server's records. In the review case, the client cannot see that a ticket exists. A missing response does not establish whether the write happened.
| Scenario | Requests | Tickets saved | Client state |
|---|---|---|---|
| Blind retry | 2 | 2 | complete |
| Same key, provider support | 2 | 1 | complete |
| Stop for review | 1 | 1 | review required |
| Provider ignores the key | 2 | 2 | complete |
| Validation rejection | 1 | 0 | rejected |
Reusing an operation key works here because the server remembers the original request and returns its receipt. The fourth case sends the same header to a server that ignores it. Two tickets are still created.
When the client cannot establish that a retry is safe, it records an uncertain result. A real workflow can reconcile that operation before deciding to send another write.
{
"client": {
"state": "complete",
"attempts": 2,
"ticket_id": "ticket-2",
"events": [
{
"attempt": 1,
"event": "outcome_uncertain",
"error_type": "RemoteDisconnected"
},
{
"attempt": 2,
"event": "receipt_received",
"http_status": 201
}
]
},
"server": {
"records": [
{
"ticket_id": "ticket-1",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
},
{
"ticket_id": "ticket-2",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
}
],
"requests": [
{
"request": 1,
"key": null,
"write": true,
"cache_hit": false,
"result": "written_then_connection_closed"
},
{
"request": 2,
"key": null,
"write": true,
"cache_hit": false,
"result": "written_then_receipt_sent"
}
]
}
}{
"client": {
"state": "complete",
"attempts": 2,
"ticket_id": "ticket-1",
"events": [
{
"attempt": 1,
"event": "outcome_uncertain",
"error_type": "RemoteDisconnected"
},
{
"attempt": 2,
"event": "receipt_received",
"http_status": 200
}
]
},
"server": {
"records": [
{
"ticket_id": "ticket-1",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
}
],
"requests": [
{
"request": 1,
"key": "same-operation",
"write": true,
"cache_hit": false,
"result": "written_then_connection_closed"
},
{
"request": 2,
"key": "same-operation",
"write": false,
"cache_hit": true,
"result": "cached_receipt"
}
]
}
}{
"client": {
"state": "review_required",
"attempts": 1,
"ticket_id": null,
"events": [
{
"attempt": 1,
"event": "outcome_uncertain",
"error_type": "RemoteDisconnected"
}
]
},
"server": {
"records": [
{
"ticket_id": "ticket-1",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
}
],
"requests": [
{
"request": 1,
"key": null,
"write": true,
"cache_hit": false,
"result": "written_then_connection_closed"
}
]
}
}{
"client": {
"state": "complete",
"attempts": 2,
"ticket_id": "ticket-2",
"events": [
{
"attempt": 1,
"event": "outcome_uncertain",
"error_type": "RemoteDisconnected"
},
{
"attempt": 2,
"event": "receipt_received",
"http_status": 201
}
]
},
"server": {
"records": [
{
"ticket_id": "ticket-1",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
},
{
"ticket_id": "ticket-2",
"payload": {
"request_id": "example-001",
"subject": "Move the workshop booking"
}
}
],
"requests": [
{
"request": 1,
"key": "same-operation",
"write": true,
"cache_hit": false,
"result": "written_then_connection_closed"
},
{
"request": 2,
"key": "same-operation",
"write": true,
"cache_hit": false,
"result": "written_then_receipt_sent"
}
]
}
}{
"client": {
"state": "rejected",
"attempts": 1,
"ticket_id": null,
"events": [
{
"attempt": 1,
"event": "rejected",
"http_status": 422
}
]
},
"server": {
"records": [],
"requests": [
{
"request": 1,
"key": "same-operation",
"write": false,
"cache_hit": false,
"result": "validation_rejected"
}
]
}
}Python's standard library. Real HTTP on 127.0.0.1. Fictional tickets. The fixture's records and key cache live in memory for each case; this experiment measures response loss, not recovery from a database or machine crash.