Skip to content

Failure Problem

The async upload creates a window where the DB row exists, the user has a valid shortCode, but the content is nowhere. If the upload fails, the paste is permanently broken.


What broken looks like

The user submits a paste. They get back:

201 Created
{ "shortCode": "aB3xYz" }

They copy the link. They share it. Meanwhile, the background S3 upload fails — S3 returns a 500, or the worker process crashes, or the network drops.

Now the DB row looks like this:

short_code:  aB3xYz
s3_url:      NULL
status:      IN_PROGRESS
content:     (not stored anywhere)

Someone clicks the link:

GET /paste/aB3xYz
  → cache miss (nothing cached yet)
  → DB lookup → row found, s3_url = NULL
  → nothing to fetch
  → ???

The read path has no content to return. The paste exists in the DB but is unreadable. The original writer has no idea — they got a 201 and assumed everything worked.


Why this is a durability problem

Durability means: once you tell a user their data is saved, it stays saved. Returning 201 Created is an implicit promise — "your paste exists, here's the shortCode."

If the async upload silently fails and the paste becomes unreadable, that promise is broken. The user's data is effectively lost — they can't retrieve it, and they don't know why.

This is the failure mode the state machine is designed to handle.


The dedup case makes it worse

Recall the dedup check: if two users submit identical content, only one S3 object is stored and both rows point to the same s3_url (via ref_count).

If the first upload fails, neither user's paste has content. Both got 201s. Both pastes are broken. The failed upload affects multiple rows simultaneously.

This is why silent failures are unacceptable — a single S3 upload failure can corrupt multiple pastes.