Vetora logo
Easy7 componentsInterview: High

Pastebin — Object Storage + NoSQL TTL

Advanced Pastebin architecture separating paste content (S3) from metadata (DynamoDB). Native TTL auto-deletes expired metadata. CDN caches popular public pastes at edge locations. Handles arbitrarily large pastes and global-scale traffic.

StorageS3DynamoDBCDNTTL
Problem Statement

The Object Storage + NoSQL TTL architecture is the advanced Pastebin design that addresses the two fundamental limitations of the RDBMS+Cache approach: large paste performance and TTL cleanup. By separating paste content (S3) from metadata (DynamoDB) and adding a CDN for edge caching, this variant handles arbitrarily large pastes, global-scale traffic, and automatic expiry without background jobs.

The key architectural insight is that paste content and paste metadata have fundamentally different access patterns and storage requirements. Content is a blob — ranging from a few bytes to 10MB — that is write-once and read-many. S3 is purpose-built for this: unlimited object size, 99.999999999% durability, and no impact on metadata query performance regardless of blob size. Metadata is small (approximately 200 bytes per paste), queried frequently, and needs TTL-based expiry. DynamoDB is purpose-built for this: single-digit millisecond reads at any scale, and native TTL that automatically deletes expired items without application intervention.

The separation solves the PostgreSQL TOAST problem from the RDBMS approach. In PostgreSQL, a 10MB paste fragments across approximately 1,250 TOAST pages. Reading such a paste requires fetching all pages — slower than a single S3 GET. More critically, when expired pastes are deleted, their TOAST pages must be vacuumed separately, consuming CPU and I/O during maintenance windows. With S3, a 10MB paste is a single object — reading and deleting it are both constant-time operations regardless of size.

DynamoDB native TTL eliminates the need for a background cleanup job. Each paste's metadata row includes an expires_at field containing a Unix epoch timestamp. DynamoDB automatically scans for and deletes items whose TTL has passed — within 48 hours of expiry, without any application logic. In the RDBMS approach, if the nightly cleanup job fails, expired pastes remain accessible and continue consuming storage. With DynamoDB TTL, expiry is a platform-level guarantee.

The CDN layer (CloudFront) adds edge caching for popular public pastes. While most Pastebin pastes have small audiences, some go viral — error logs shared on social media, code snippets referenced in blog posts, configuration files linked in documentation. Without a CDN, these viral pastes hammer the origin at thousands of requests per second. CloudFront absorbs this traffic at edge locations globally, serving cached content in approximately 5ms. The CDN applies only to public pastes — password-protected pastes bypass CloudFront entirely via Cache-Control: no-cache headers.

This architecture supports 10,000+ sustained RPS with global distribution. S3 and DynamoDB both scale automatically — there is no connection pool ceiling as in PostgreSQL. The trade-off is higher complexity (7 components vs 5 in the RDBMS approach) and higher per-request latency for small pastes (S3 GET at approximately 50ms vs PostgreSQL indexed read at approximately 12ms). For services where most pastes are small and traffic is moderate, the RDBMS+Cache approach is more cost-effective. For services handling large pastes, global traffic, or requiring zero-maintenance TTL cleanup, the Object Storage approach is the right choice.

Architecture Overview

The Object Storage + NoSQL TTL system uses seven components organized into a multi-tier architecture: Client, CDN (CloudFront), Load Balancer, PasteService, Redis HotCache (for metadata), DynamoDB (for metadata with native TTL), and S3 (for paste content). The key architectural decision is the separation of content storage from metadata storage, with each using the optimal service for its access pattern.

All traffic enters through the CDN (CloudFront). For public paste reads, CloudFront checks its edge cache first. At approximately 30% hit rate for popular pastes, about one-third of read traffic never reaches the origin. On CDN miss, the request routes to the Load Balancer, which distributes across PasteService pods. Paste creation requests and password-protected paste reads bypass the CDN and go directly to the Load Balancer.

PasteService is the central coordinator running on 4 pods with 100 threads each. The write path executes two sequential operations: (1) upload paste content to S3 as an individual object keyed by paste_id (approximately 100ms for a 5KB paste), then (2) write metadata to DynamoDB with the s3_key reference and native TTL set to the configured expiry timestamp (approximately 10ms). Content is written to S3 first because if the DynamoDB write fails, an orphaned S3 object is less harmful than a metadata entry pointing to nonexistent content. On successful write, PasteService also populates the Redis HotCache with the metadata for immediate read availability.

The read path has three tiers. First, PasteService checks the Redis HotCache for paste metadata (approximately 85% hit rate, 1ms latency). On cache miss, it queries DynamoDB for metadata (approximately 5ms). If the metadata exists (not TTL-deleted), PasteService fetches the paste content from S3 (approximately 50ms). The metadata cache stores only small records (paste_id, syntax, expires_at, s3_key, is_public) — content is always fetched from S3, keeping the Redis working set small and hit rate high.

DynamoDB runs in on-demand capacity mode, scaling automatically with traffic. The paste_metadata table has paste_id as the partition key and includes an expires_at field configured as the DynamoDB TTL attribute. DynamoDB automatically deletes expired items in the background — typically within a few hours of expiry, guaranteed within 48 hours. No background job, no cron, no operational burden. The application-level expiry check in PasteService is a safety net for the lag between expiry time and actual DynamoDB deletion.

S3 stores paste content as individual objects in a single bucket. S3 lifecycle policies are configured to delete objects older than the maximum paste lifetime (1 year), catching any orphaned objects from failed DynamoDB writes. S3 provides 99.999999999% (11 nines) durability and scales to any request rate without connection pool constraints. GET latency is approximately 50ms — higher than PostgreSQL's 12ms indexed read, but consistent regardless of object size.

Architecture Preview
Loading architecture preview...
Key Design Decisions
Content/Metadata Separation

Choice

Paste content in S3, metadata in DynamoDB

Rationale

Content (up to 10MB blobs) and metadata (~200 bytes per paste) have fundamentally different storage requirements. S3 handles arbitrarily large objects without impacting metadata query performance. DynamoDB provides single-digit millisecond metadata reads at any scale. In PostgreSQL, large pastes fragment across TOAST pages, slowing reads and increasing vacuum overhead.

DynamoDB Native TTL

Choice

expires_at field configured as DynamoDB TTL attribute for automatic item deletion

Rationale

DynamoDB TTL automatically deletes expired items without any application logic or background job. In the RDBMS approach, a nightly cleanup job is required — if it fails, expired pastes remain accessible. DynamoDB TTL is a platform-level guarantee with eventual consistency (items deleted within 48 hours of expiry). The application checks expiry before serving as a safety net for the deletion lag.

CDN for Public Pastes

Choice

CloudFront edge-caches popular public pastes with 300s TTL

Rationale

While most pastes have small audiences, viral pastes (error logs on social media, code in blog posts) generate thousands of requests per second. CloudFront absorbs these traffic spikes at edge locations (~5ms latency) without hitting the origin. Password-protected pastes bypass the CDN via Cache-Control: no-cache. The ~30% CDN hit rate significantly reduces origin load during traffic spikes.

Redis for Metadata Caching Only

Choice

Cache paste metadata (~200 bytes) in Redis, always fetch content from S3

Rationale

Caching only metadata keeps the Redis working set small (~2GB for millions of metadata entries) and hit rate high (~85%). Caching content would require 10-100x more memory for diminishing returns — S3 GET latency (~50ms) is consistent and acceptable. The metadata cache eliminates the DynamoDB read cost on 85% of requests while keeping infrastructure costs low.

S3-First Write Order

Choice

Upload content to S3 before writing metadata to DynamoDB

Rationale

If the DynamoDB write fails after S3 upload, an orphaned S3 object exists but no metadata points to it — the paste simply does not exist from the user's perspective. S3 lifecycle policies clean up orphans. If the order were reversed (DynamoDB first), a metadata entry could point to nonexistent content, causing 500 errors on read.

Scale & Performance

Target RPS

10K+ sustained RPS

Latency (p99)

<10ms CDN hit, ~80ms full path (metadata + S3)

Storage

Unlimited (S3 scales automatically)

Availability

99.95% (S3 + DynamoDB + CDN HA)

Time & Space Complexity
OperationTimeSpaceNotes
Create paste (POST /api/v1/pastes)O(1) S3 PUT + O(1) DynamoDB PutItem + O(1) cache SETO(1) per paste (content in S3 + ~200 bytes metadata in DynamoDB)~122ms total (100ms S3 PUT + 10ms DynamoDB + 2ms cache + 10ms network). S3 PUT is the bottleneck.
Read paste — CDN hit (GET /api/v1/pastes/{id})O(1) CDN edge cache lookupO(1) per read~5ms from nearest edge location. ~30% of public paste reads follow this path.
Read paste — cache hit (GET /api/v1/pastes/{id})O(1) cache GET + O(1) S3 GETO(1) per read~61ms total (1ms cache + 50ms S3 + 10ms network). ~60% of reads follow this path (85% of non-CDN reads).
Read paste — full miss (GET /api/v1/pastes/{id})O(1) DynamoDB GetItem + O(1) S3 GET + O(1) cache SETO(1) per read + O(1) cache entry~67ms total (5ms DynamoDB + 50ms S3 + 2ms cache backfill + 10ms network). ~10% of reads follow this path.
Database Schema (HLD)
paste_metadata (DynamoDB)

Metadata store for all pastes. Small items (~200 bytes) enable fast reads and low DynamoDB costs. Native TTL on expires_at automatically deletes expired items. On-demand capacity scales automatically with traffic.

paste_id VARCHAR (partition key, Base62-encoded UUID)s3_key VARCHAR (S3 object key for content)syntax VARCHAR (syntax highlighting hint)expires_at NUMBER (Unix epoch for DynamoDB TTL)password_hash VARCHAR (nullable — bcrypt hash)is_public BOOLEAN (CDN eligibility flag)created_at VARCHAR (ISO timestamp)

Partition: paste_id

On-demand capacity. Items ~200 bytes. DynamoDB TTL deletes expired items within 48h of expiry. No background cleanup needed.

paste_content (S3 Bucket)

Blob storage for paste content. Each paste is an individual S3 object keyed by paste_id. Handles pastes from a few bytes to 10MB with consistent ~50ms GET latency regardless of size.

paste_id (S3 object key, matches DynamoDB partition key)content (object body — paste text content)Content-Type (metadata — text/plain or application/octet-stream)

S3 Standard storage class. Lifecycle policy deletes objects >1 year old. 99.999999999% durability. ~$0.023/GB/month.

meta:{paste_id} (Redis HotCache)

Cache-aside store for hot paste metadata only. Content is always fetched from S3 separately. Small entries (~200 bytes) keep memory usage low and hit rate high (~85%).

syntax STRING (syntax highlighting hint)expires_at STRING (ISO timestamp for app-level check)s3_key STRING (S3 object key for content fetch)is_public STRING (CDN eligibility flag)

Working set ~2GB across 2 nodes (13GB each). Redis TTL matches paste expiry. Metadata-only caching maximizes hit rate.

Solution Comparison
VariantTierLatencyThroughputCostComplexityReliability
Naive (Single Service + SQL)T112ms-200ms+ reads~500 sustained RPS$200/month (single DB + service)Low — 4 components, no cache99% (single DB, no failover)
RDBMS with Cache (Postgres + Redis)T2<5ms cache hit, 12ms miss~5K sustained RPS$600/month (DB + Redis + service)Medium — cache-aside pattern99.9% (Redis HA, DB replicas)
Object Storage + NoSQL TTLT3<10ms CDN hit, ~80ms full path10K+ sustained RPS$1,200/month (S3 + DynamoDB + CDN + Redis)High — 7 components, two-system storage99.95% (S3 durability, DynamoDB HA)

This template is for educational and illustration purposes only. It may not represent the optimal production design for this problem. Real-world systems involve additional considerations (compliance, specific cloud provider constraints, organizational requirements) not captured here. Use this as a starting point for discussion, not as a production blueprint.

Frequently Asked Questions
Why is S3 GET latency (50ms) acceptable when PostgreSQL reads are 12ms?

The 50ms S3 GET latency is higher than PostgreSQL's 12ms indexed read for small pastes. However, S3 latency is consistent regardless of object size — a 10MB paste takes the same ~50ms as a 5KB paste. PostgreSQL's 12ms applies to small pastes but degrades significantly for large pastes due to TOAST page fetches. Additionally, the CDN absorbs popular paste reads at ~5ms, and the Redis metadata cache eliminates DynamoDB latency on 85% of requests. The total read path is ~80ms worst case, well within the 200ms p99 SLO.

How does DynamoDB native TTL compare to PostgreSQL background cleanup?

DynamoDB TTL is a platform-managed feature that automatically scans for and deletes items past their TTL timestamp. Items are typically deleted within a few hours of expiry, guaranteed within 48 hours. No application code, no cron job, no operational burden. PostgreSQL has no native TTL — expired rows must be deleted by a background cleanup job that the application team manages. If the job fails, crashes, or falls behind, expired pastes remain accessible and consume storage indefinitely.

When should you use this approach instead of the RDBMS+Cache variant?

Use Object Storage + NoSQL TTL when: (1) paste sizes regularly exceed 8KB, where PostgreSQL TOAST adds vacuum overhead; (2) you need zero-maintenance TTL cleanup without background jobs; (3) traffic is global and CDN edge caching would significantly improve latency; (4) storage needs exceed what is cost-effective in PostgreSQL (S3 is ~$0.023/GB/month vs RDS at ~$0.10/GB/month). For small pastes at moderate scale in a single region, the RDBMS+Cache approach is simpler and faster.

What happens if metadata and content become inconsistent?

The S3-first write order minimizes inconsistency risk. If S3 upload succeeds but DynamoDB write fails, an orphaned S3 object exists — harmless, cleaned up by S3 lifecycle policies. If DynamoDB write succeeds but S3 upload somehow fails (extremely rare given S3 durability), metadata points to nonexistent content — PasteService returns 404 on the content fetch and logs the inconsistency for manual resolution. In practice, this scenario is vanishingly rare and self-healing via lifecycle policies.

Why not use S3 pre-signed URLs to serve content directly from S3?

Pre-signed URLs would allow clients to fetch paste content directly from S3, bypassing PasteService on the read path. This reduces origin load but has two drawbacks: (1) password-protected pastes cannot be validated — the pre-signed URL grants access to anyone who has it; (2) TTL/expiry checks happen in PasteService, and a pre-signed URL could serve expired content if the S3 object has not been deleted yet. For public, non-expiring pastes, pre-signed URLs via CloudFront are a valid optimization.

Related Templates

Discussion

Sign in to join the discussion.

Ready to design your own Pastebin?

Open the simulator, place components on the canvas, wire them up, and run a traffic simulation to see how your architecture performs under real load.

Open Simulator