Loading page...Загружаем страницу...正在加载页面...
AtlasRepo

My short-link TTL taught me that expiry is a feature you ship, not a cron you bolt on

Description and use

Implementing TTL for short links revealed issues with clock skew, process dependency, and race conditions. The solution was to enforce TTL in the query and use the database as the source of truth, with a sweeper only reclaiming expired rows.

Problem

Implementing temporary short links with TTL required reliable expiry handling, but naive background sweeper caused clock skew, process‑dependent expiry, and race conditions.

Outcome

TTL now reliably expires links regardless of clock skew, process restarts, or race conditions; expired or non‑existent links uniformly return 410.

Workflow

  • 1. Store link with optional expires_at timestamp.
  • 2. On redirect request, query WHERE slug = ? AND (expires_at IS NULL OR expires_at > now()).
  • 3. If row exists, serve redirect; else return 410 Gone.
  • 4. Periodic sweeper runs to delete rows where expires_at < now() (garbage collection).

Evidence

First, clock skew. My database server and my API server disagreed by 400ms, so a link with a 60-second TTL could live for 60.4 seconds or die at 59.6 depending on which process touched it last.

Source

Second, the sweeper only ran while the process was alive. Restart the bot, and expiry silently stopped.

Source

Third, and this is the one that actually bit me: deletion wasn't atomic with lookup.

Source

The fix wasn't a transaction — it was moving the expiry check into the query itself: WHERE slug = ? AND (expires_at IS NULL OR expires_at > now()).

Source

  • Record date: 2026-10-18T19:41:27.149Z