Lab: Build and Test a URL Shortener
This hands-on lab transitions the theoretical URL Shortener system design into a running execution environment. By generating functional short links and executing a load test of 200 sequential requests, you will observe the direct operational impact on the underlying database and analyze real-world system behavior during request processing.
1. Purpose of the Hands-On Lab
System design interviews require both theoretical knowledge of architecture diagrams and an understanding of how components interact at runtime. The goal of this lab is to bridge that gap by executing the exact URL shortener service designed in the previous theoretical module.
Core Objectives
- System Deployment: Spin up the URL shortener application service alongside a persistent relational database.
- Link Generation (Write Path): Submit original URLs to the system and observe the creation of unique, shortened keys.
- Redirection Execution (Read Path): Issue requests using generated short keys and verify HTTP redirection behavior.
- Database Observability: Send 200 automated requests to a single short link and monitor real-time database queries to analyze read performance and resource utilization.
2. High-Level URL Shortener Architecture Review
Before executing the lab, review the core components powering the URL shortener service:
- Write Path: Receives a long target URL (e.g.,
https://example.com/very/long/path), generates a unique base-62 short key (e.g.,aX9k2L), and persists the mapping in the database. - Read Path: Intercepts requests sent to
http://short.url/{shortKey}, queries the database for the matching original URL, and responds with an HTTP redirection header (301 Moved Permanentlyor302 Found).
3. Step-by-Step Lab Execution Workflow
The lab execution flow moves from service initialization to URL creation, automated traffic generation, and database metric observation:
4. The Write Path: Generating Short Links
To create a short link, the client issues an HTTP POST request to the API endpoint with the original URL in the payload.
API Request Example
POST /api/v1/shorten HTTP/1.1
Host: localhost:8080
Content-Type: application/json
{
"long_url": "https://example.org/articles/system-design-fundamentals-2026"
}
API Response Example
HTTP/1.1 201 Created
Content-Type: application/json
{
"short_key": "k83Jz1",
"short_url": "http://localhost:8080/k83Jz1",
"long_url": "https://example.org/articles/system-design-fundamentals-2026",
"created_at": "2026-03-30T10:15:30Z"
}
Database Storage Schema
The database stores this association in a standard relational mapping table:
CREATE TABLE url_mapping (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
short_key VARCHAR(10) NOT NULL UNIQUE,
original_url TEXT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
5. The Read Path: HTTP Redirection Mechanics
When a user visits http://localhost:8080/k83Jz1, the web service intercepts the key k83Jz1 and queries the persistent store.
Redirection Request & Response Flow
Server HTTP Response
HTTP/1.1 302 Found
Location: https://example.org/articles/system-design-fundamentals-2026
Cache-Control: no-cache
Note: A
302 Foundstatus code forces the client browser to request the original URL while requiring future clicks to return through the shortener service. A301 Moved Permanentlyallows browsers to cache the destination locally, bypassing the application server on subsequent visits.
6. Executing the 200-Request Load Experiment
In this stage of the lab, you simulate incoming traffic by firing 200 HTTP requests in rapid succession to evaluate system performance without application-level caching.
Automating Requests via Bash Script
Use the following shell loop to simulate the 200 incoming read requests to your short link:
#!/usr/bin/env bash
TARGET_URL="http://localhost:8080/k83Jz1"
TOTAL_REQUESTS=200
echo "Starting load test: sending $TOTAL_REQUESTS requests to $TARGET_URL..."
for i in $(seq 1 $TOTAL_REQUESTS)
do
# Perform request without following redirection (-s silent, -o suppress output)
curl -s -o /dev/null -w "Request #$i - HTTP Status: %{http_code}\n" "$TARGET_URL"
done
echo "Load test complete."
7. Analyzing Database Impact and Bottlenecks
During the 200-request execution, monitoring tools display database activity metrics. Because this basic deployment lacks an in-memory caching layer (such as Redis or Memcached), every single HTTP request triggers a physical database read.
Key Observations
- 1:1 Request-to-Query Ratio: 200 HTTP GET requests lead to exactly 200
SELECTqueries against the relational database. - Database Connection Overhead: High concurrency causes thread contention and connection pool exhaustion as queries queue up waiting for execution.
- I/O Overhead: Repeatedly querying disk-backed storage for the exact same static mapping wastes CPU cycles and disk I/O bandwidth.
Real-World Engineering Takeaway
In high-throughput environments processing millions of requests per second, routing read traffic directly to a relational database will cause database exhaustion and high latency. This experiment highlights the necessity of introducing an In-Memory Caching Layer (e.g., Redis) between the application service and the database to serve read requests with sub-millisecond responses.
8. Tabular Synthesis: Uncached vs. Cached Redirection
9. Summary and Key Takeaways
- Theory to Practice: Running the deployed URL shortener provides practical context for system design principles, validating routing logic and persistence mechanisms.
- Direct Database Routing: Without caching, every short link redirection requires an explicit
SELECTquery against the database storage layer. - Linear Load Impact: Generating 200 requests creates 200 distinct database queries, demonstrating how fast un-cached architectures exhaust database resources.
- HTTP Status Codes: Using
302 Foundredirects keeps control with the shortener service for analytics tracking, while301 Moved Permanentlyallows browsers to cache redirects locally. - Need for In-Memory Caching: Observing database performance under load demonstrates why production system designs place distributed caches (e.g., Redis) in front of primary databases.