Link Types and Lifecycle
Link Types
The system supports two types of short links:| Link Type | Owner | Expiration | Use Case |
|---|---|---|---|
| TEMPORARY | Anonymous (no user) | 8 hours from creation | Quick, one-time sharing without registration |
| PERMANENT | Registered user | Never expires | Long-term URLs for authenticated users |
Link Status Lifecycle
Every short URL goes through the following status lifecycle:Status Definitions:
- ACTIVE: Link is live and accepting redirects
- EXPIRED: Temporary link has passed its 8-hour TTL
- DELETED: User soft-deleted the link (can be purged)
Anonymous Link TTL (Time-to-Live)
Anonymous links are designed for temporary sharing and automatically expire after 8 hours.
Configuration
Backend:ShortUrlServiceImpl.java:48
localUrlStore.ts:30
How It Works
-
Creation: When an anonymous user creates a short URL via
POST /api/urls/public, the backend:- Sets
expiresAt = now + 8 hours - Sets
linkType = TEMPORARY - Sets
userId = null - Sets
status = ACTIVE
- Sets
-
Client Storage: The frontend stores the URL in
localStoragewith the same 8-hour expiration -
Expiration Check: On every redirect attempt (
GET /{shortCode}), the backend:- Checks if
expiresAt < now - If expired, returns HTTP 410 Gone (not 404)
- Updates status to
EXPIREDin database
- Checks if
- Cleanup: A scheduled job runs every 15 minutes to permanently delete expired links
Example Timeline
| Time | Event |
|---|---|
| 00:00 | Anonymous user creates link, expiresAt = 08:00 |
| 00:00 - 07:59 | Link is ACTIVE, redirects work normally |
| 08:00 | Link expires |
| 08:00+ | Redirect attempts return HTTP 410 Gone |
| 08:15 | Cleanup job marks link as EXPIRED |
| 08:15+ | Cleanup job hard-deletes the expired link |
Permanent Links
Registered users create permanent links that never expire.
Characteristics
- Ownership: Linked to authenticated user via
userId - Expiration:
expiresAt = null(never expires) - Link Type:
linkType = PERMANENT - Visibility: Only visible to the owner in their dashboard
- Deletion: Owner can soft-delete via
DELETE /api/urls/{id}
Anonymous to Permanent Conversion
When a user registers or logs in, their anonymous URLs fromlocalStorage are synced to their account via POST /api/urls/sync:
- Frontend sends array of original URLs from
localStorage - Backend claims existing anonymous entries OR creates new ones
- Claimed entries are updated:
userIdset to authenticated userlinkTypechanged toPERMANENTexpiresAtset tonull
- Frontend clears
localStorage
ShortUrlServiceImpl.java:176-219
HTTP Status Codes for Links
Redirect Endpoint (GET /{shortCode})
The redirect endpoint returns different HTTP status codes based on link state:
| Status Code | Condition | Meaning |
|---|---|---|
| 302 Found | Link is ACTIVE and valid | Successful redirect to originalUrl |
| 404 Not Found | Short code doesn’t exist | Link was never created or already hard-deleted |
| 410 Gone | Link is EXPIRED | Link existed but has expired (temporary link past 8h TTL) |
Why 410 instead of 404 for expired links?HTTP 410 Gone explicitly indicates the resource existed in the past but is permanently unavailable. This is semantically correct for expired links and helps search engine crawlers understand the link won’t return.From RFC 7231:
“The 410 response is primarily intended to assist the task of web maintenance by notifying the recipient that the resource is intentionally unavailable and that the server owners desire that remote links to that resource be removed.”
RedirectController.java + GlobalExceptionHandler.java
Automated Cleanup Jobs
Cleanup Schedule
Service:ExpiredUrlCleanupService.java
Schedule: Every 15 minutes with 1-minute initial delay
Lock Configuration:
lockAtLeastFor: 10 minutes (prevents immediate re-execution)lockAtMostFor: 14 minutes (safety timeout if job hangs)
Two-Phase Cleanup Strategy
The cleanup uses a two-phase approach to prevent race conditions with redirect checks:
Why Two Phases?
This prevents a race condition where:- User requests redirect for expiring link
- Cleanup job deletes the row mid-request
- Redirect endpoint queries for the link
- Gets 404 (not found) instead of 410 (expired)
ShedLock Configuration
ShedLock stores distributed lock state in PostgreSQL: Table:shedlock (created by db/shedlock.sql)
Configuration: AppConfig.java:25
ShedLock Table Schema
ShedLock Table Schema
Welford’s Algorithm for Latency Tracking
The system tracks average redirect latency using Welford’s online algorithm for computing a running mean without storing historical data.
Why Welford’s Algorithm?
Traditional averaging requires storing all values or maintaining a sum and count. Welford’s algorithm computes the mean incrementally with:- O(1) space (only stores current mean and count)
- O(1) time per update
- Numerically stable (avoids overflow/underflow)
Implementation
Location:ShortUrlServiceImpl.java:130-132
How It Works
Formula:μₙ = μₙ₋₁ + (xₙ − μₙ₋₁) / n
Where:
μₙ= new average after n measurementsμₙ₋₁= previous averagexₙ= new measurement (current redirect latency)n= total count of measurements
| Redirect # | Latency (ms) | Previous Avg | Calculation | New Avg |
|---|---|---|---|---|
| 1 | 150 | 0.0 | 0 + (150 - 0) / 1 | 150.0 |
| 2 | 200 | 150.0 | 150 + (200 - 150) / 2 | 175.0 |
| 3 | 180 | 175.0 | 175 + (180 - 175) / 3 | 176.67 |
| 4 | 160 | 176.67 | 176.67 + (160 - 176.67) / 4 | 172.5 |
Latency Measurement
Measurement Point:RedirectController.java
The latency is measured from when the redirect controller receives the request until just before returning the redirect response:
This measures server-side processing time only, not network round-trip time or client rendering time.
Soft Delete
Deleted links are not immediately removed from the database. They are marked as DELETED and purged by the cleanup job.
Soft Delete Process
- User calls
DELETE /api/urls/{id}with JWT token - Backend verifies ownership (
userIdmatches authenticated user) - Status changes from
ACTIVEtoDELETED - Link is no longer visible in user’s dashboard
- Redirects to deleted links return 404 Not Found
- Next cleanup job (≤15 min) hard-deletes the row
ShortUrlServiceImpl.java:157-171
Why Soft Delete?
Benefits:- Audit trail: Temporarily preserves data for debugging/analytics
- Undo capability: Could implement “restore” feature
- Analytics: Redirect counts remain accurate during cleanup window
- Race condition safety: Redirect checks see DELETED status vs. missing row
- Deleted data remains up to 15 minutes
- Requires cleanup job to purge
Short Code Generation
Algorithm
Service:ShortCodeGenerator.java
Character Set: [a-zA-Z0-9] (62 characters)
Length: 7 characters (default)
Collision Space: 62^7 = 3,521,614,606,208 possible codes (~3.5 trillion)
Collision Handling
Max Retries: 5 attempts Implementation:ShortUrlServiceImpl.java:228-238
Collision Probability: With 7 alphanumeric characters, the probability of collision remains negligible until hundreds of millions of links are created.
Analytics Tracking
Each short URL tracks the following analytics:| Metric | Type | Description |
|---|---|---|
redirectCount | Long | Total number of times the link was accessed |
avgRedirectMs | Double | Running average redirect latency (Welford’s algorithm) |
destinationStatus | Integer | Last HTTP status of destination URL health check |
lastCheckedAt | Instant | Timestamp of last destination availability check |
ShortUrl.java:103-126
Global Platform Statistics
The system provides public aggregate statistics viaGET /api/stats:
| Stat | Description |
|---|---|
totalLinks | Count of all ACTIVE short URLs (both temporary and permanent) |
totalRedirects | Sum of all redirectCount across all links |
avgLatencyMs | Global average redirect latency across all links |
ShortUrlRepository.java (JPQL aggregates)
Security Rules
Password Hashing
Algorithm: BCrypt Cost Factor: 12 (OWASP recommendation) Configuration:SecurityConfig.java:59-61
JWT Token Validation
Algorithm: HS256 (HMAC-SHA256) Minimum Secret Length: 32 characters (256 bits) Default Expiration: 24 hours (86400000 ms) Validation:JwtTokenProvider.java:79-95
Tokens are validated on every authenticated request via JwtAuthenticationFilter:
- Signature verification
- Expiration check
- Subject (email) extraction
Access Control
Users can only:- View their own short URLs
- Delete their own short URLs
- Create new short URLs (authenticated or anonymous)
ShortUrlServiceImpl.java:157-171
Business Rule Summary
Quick Reference Table
Quick Reference Table
| Rule | Value | Location |
|---|---|---|
| Anonymous TTL | 8 hours | ShortUrlServiceImpl.java:48, localUrlStore.ts:30 |
| Cleanup Job Interval | 15 minutes | ExpiredUrlCleanupService.java:44 |
| ShedLock Duration | 10-14 min | ExpiredUrlCleanupService.java:45 |
| JWT Expiration | 24 hours | application.yml:50 |
| JWT Algorithm | HS256 | JwtTokenProvider.java |
| JWT Min Secret Length | 32 chars | JwtTokenProvider.java |
| BCrypt Cost Factor | 12 | SecurityConfig.java:60 |
| Short Code Length | 7 chars | ShortCodeGenerator.java |
| Short Code Charset | [a-zA-Z0-9] | ShortCodeGenerator.java |
| Collision Retries | 5 | ShortUrlServiceImpl.java:45 |
| Expired Link HTTP Code | 410 Gone | GlobalExceptionHandler.java |
| Missing Link HTTP Code | 404 Not Found | GlobalExceptionHandler.java |
| Redirect HTTP Code | 302 Found | RedirectController.java |
| Connection Pool Max | 10 | application.yml:18 |
| Connection Pool Min Idle | 2 | application.yml:19 |
Related Documentation
- Environment Variables - Configuration reference
- API Authentication - Authentication endpoints
- URL Management - URL management endpoints