System designby Learnastra

System-design interview · Core interviews

Design Pastebin

By Anup Rai

Design immutable text publication with metadata, byte storage, private access, expiry and recoverable cleanup; compare a single transaction with a two-store publication protocol.

You will learn to

  • Trace text bytes separately from the record that makes them visible.
  • Choose a storage boundary using average size, maximum size, and retention arithmetic.
  • Recover from uploads and deletions interrupted between two stores.

Practice in this chapter

8 interview questions with model answers and follow-ups.

Go to interview practice

Useful foundations: Design a URL shortener · Databases, data models, and ACID transactions · Message queues, event logs, delivery guarantees, and backpressure

Workload and timing examples are interview assumptions.

01Problem and scope

A paste service accepts text, stores it durably, and returns a stable URL from which authorized readers can retrieve the exact saved bytes. Unlike a URL shortener, the service owns the content as well as the identifier. The design must therefore cover upload limits, publication, access checks, rendering safety and byte reclamation. For example, creating a diagnostic log returns https://paste.example/p/p7Hk2Lm9; reading that URL must return the complete saved text. Metadata describes the text—owner, length, title, visibility and expiry—rather than containing the text bytes themselves.

The scope is immutable text up to 10 MB, with public, unlisted and explicitly private access, optional custom aliases, and expiration. Clarify these visibility modes before choosing a cache: unlisted means omitted from discovery but accessible to anyone with the address; private means an authenticated grant is required. Forwarding an unlisted URL does not preserve team membership restrictions.

We support a website and programmatic API. Images, collaborative editing, full-text public discovery and executable code previews are outside this interview. Optional titles, owner listings and approximate view counts remain in scope. Request rates are modest, but retained text grows large. If metadata and bytes move to different stores, creation and deletion must remain recoverable when only one store finishes its work.

02Functional requirements

  1. Create with request key: One paste identity for repeated attempts of the same payload.
  2. Choose custom alias: Claim if unused; conflict rather than overwrite another paste.
  3. Read public/unlisted: No login required; visibility still obeys expiry and deletion.
  4. Read private: Authenticated current grant is required before bytes are delivered.
  5. Delete: Owner revokes new reads; physical bytes may be reclaimed later.
  6. List or inspect statistics: Owner-only cursor pages and explicitly delayed counters.

Publication and text delivery

A paste is visible only after the complete intended content exists durably. The owner may upload a large paste over a slow connection without occupying every read-serving worker. The reader receives either the exact saved text or an explicit error; the application must not render a truncated upload as a successful paste. A browser page escapes text, while the raw endpoint returns a plain-text response.

Immutability, expiry and ownership

Immutable text avoids concurrent-edit semantics. Updating a paste means creating a new version with a new identity, not mutating an already cached body. Automatic expiry is evaluated during reads, even if cleanup is delayed. Never reassign an expired alias to a stranger: old incident notes could otherwise point to unrelated text. Anonymous creation is possible with stricter quotas and a separate deletion secret; this worked design uses authenticated owners so ownership and retry identities stay clear.

03Non-functional requirements

  1. Latency: Metadata lookup p95 below 100 ms; time to first byte p95 below 200 ms inside the serving region. Whole-response time is separate: 10 MB over 10 Mb/s already takes roughly eight seconds before overhead.
  2. Availability: 99.9% eligible read availability.
  3. Durability: Acknowledge creation only after configured replicated storage accepts it. Accepted content survives one storage-node or availability-zone failure.
  4. Regional recovery: Use tested backups with an initial one-hour restore objective and an explicitly measured backup recovery point.
  5. Private authorization: Check current authoritative metadata on every new request. A completed grant revocation blocks requests begun afterward; already delivered bytes cannot be recalled.
  6. Public visibility: Public/unlisted reads have a 30-second bounded visibility-cache policy. Use safe server time for expiry; no cache lifetime may exceed the paste deadline.
  7. Failure behavior: If a storage partition prevents current private authorization, return unavailable rather than guessing.

Publication and read invariants

Publication is the decision that a stored upload may be served. In the later two-store design, READY records that committed decision; merely uploading an object is not enough. These invariants keep the stored bytes, access policy and cleanup work in agreement.

Invariant Consequence
READY ⇒ verified immutable object exists Never expose a partial upload as a completed paste.
One request identity, one paste A retried create resolves to its original result.
Visibility checked before delivery Cached bytes still obey the paste's applicable policy.
Publication and cleanup check the same metadata state Never remove an object while an uploader can still mark that generation READY.

These rules define crash behavior more precisely than the uptime percentage. The UI distinguishes uploading, ready, expired/deleted and temporarily unavailable so a service fault is not mistaken for data loss.

04Capacity estimates

Workload assumptions and arithmetic

Use these workload assumptions: one million pastes/day, five reads per paste, 10 KB average content and 10 MB maximum. 1M / 86,400 = 11.6 creations/s and 5M / 86,400 = 57.9 reads/s. A tenfold peak gives 116 creations/s and 579 reads/s. These rates fit a credible initial application; capacity pressure comes from long retention and the largest objects.

Worked estimates

Quantity Worked result Consequence
New content 1M × 10 KB = 10 GB/day Bytes accumulate despite low QPS
Ten-year logical content 10 GB × 365 × 10 = 36.5 TB Separate bulk bytes when operationally useful
Allocation at 70% utilization 36.5 / 0.70 = 52.1 TB Preserve headroom before replication
Three content copies 36.5 × 3 = 109.5 TB Durability has a separate storage multiplier
Average egress 5M × 10 KB / 86,400 = 0.579 MB/s Average bandwidth is modest
Peak all-max-size upload scenario 116 × 10 MB = 1.16 GB/s Enforce both request and byte quotas

Capacity implications and limits

The last row is a stress case, not the expected average. If 116 uploads/s each take five seconds, roughly 580 uploads are active concurrently. Buffering 10 MB for all of them could require 5.8 GB just for request bodies. Stream bytes with bounded buffers and admission control. For caching, 100,000 distinct hot 10 KB pastes need 1 GB of body bytes plus overhead. A percentage of read requests does not establish the number of distinct cached objects; measure unique hot IDs and byte-hit rate—the fraction of requested bytes served from cache—instead.

05APIs and contracts

Request and response example

The owner sends POST /v1/pastes with key paste-create-88 and {"text":"service-started\nrequest-42 failed","title":"Checkout log","visibility":"private","expiresAt":"2027-01-01T00:00:00Z"}. The service hashes the validated payload, including visibility and expiry, for request-identity checks. A repeated key with different content returns 409. Small requests can complete synchronously; if publication has not completed by the response budget, return 202 with the existing paste identity and status endpoint, never a false ready response.

Interface contracts

Interface Contract
POST /v1/pastes 201 with {id:"p7Hk2Lm9",state:"ready"} or 202 while pending
GET /v1/pastes/p7Hk2Lm9/status Owner-only publication state and retry guidance
GET /p/p7Hk2Lm9 Authorized, escaped text page
GET /v1/pastes/p7Hk2Lm9/raw text/plain bytes with no executable interpretation
DELETE /v1/pastes/p7Hk2Lm9 Repeated owner deletion succeeds harmlessly
GET /v1/pastes?after=<cursor>&limit=50 Owner-scoped (createdAt,id) cursor

Validation and response semantics

Reject oversized content with 413, invalid text/alias with 400, exhausted quotas with 429, and temporary storage inability with 503. Private unauthorized reads should avoid disclosing whether a guessed ID exists. Custom aliases have a length and character policy; generated aliases use a larger random space and still rely on atomic uniqueness. Programmatic clients use scoped credentials and the same byte/request limits as the website, not an unrestricted “developer key” bypass.

06Data model and access patterns

This is the model used after text bytes move out of the database. The Paste row connects an object to its owner and visibility state; CreateRequest preserves the result of a retried submission; Grant answers who may read private content. The cleanup outbox is a database record of deletion work, committed with the metadata change so a crash cannot lose the obligation to remove unused bytes.

Record and fields Responsibility / constraint
Paste(id, ownerId, createdAt, objectKey, byteLength, checksum, title, visibility, expiresAt, state, uploadGeneration, leaseUntil, version) Owns paste metadata and publication state.
CreateRequest(ownerId, requestKey, payloadHash, pasteId) Unique owner/key identity.
Grant(pasteId, readerId) Authorizes private reads.
CleanupOutbox(eventId, objectKey, pasteId, generation, state) Makes deletion work recoverable.

The reader first loads the paste ID and grant through an indexed query; possession of an object key does not establish permission.

Use (ownerId,createdAt,id) for owner pagination and (state,leaseUntil,id) for stale-upload reconciliation. An upload lease is the server-recorded deadline before which that upload generation is allowed to become READY. Reconciliation means checking interrupted uploads and either completing or cancelling them from their recorded state. An expiry bucket index avoids scanning billions of rows. Byte objects have immutable generation-specific names such as pastes/p7Hk2Lm9/g1/body; they are not publicly readable by possession of their storage key. Store expected size and a real checksum separately. Do not assume an object-store ETag always equals the full content hash; multipart and encryption modes can differ.

The metadata row is authoritative for whether a paste may be served. The object store is authoritative for the actual bytes. Rendered HTML, caches and counters are rebuildable. Initially all metadata transactions run in one relational database. If it is later partitioned, keep each paste, grant set and cleanup state at the same paste owner, and use a recoverable request-reservation protocol when an owner-scoped retry record cannot be colocated. Our present workload does not require that extra distribution immediately.

07Basic working design

Single-database publication

The minimum correct service has one app and one SQL database. The owner's POST validates UTF-8 and byte length, then inserts the text, metadata and request result in one transaction. The commit is the publication point. Before it, the reader cannot see the paste. After it, a lost HTTP response is handled by looking up paste-create-88 and returning p7Hk2Lm9. A conditional unique insert, rather than a preceding absence check, decides an alias race.

Authorized text reads

The reader's raw GET reads the committed row, checks visibility, grant, expiry and deletion, and streams the stored text. The HTML endpoint escapes it rather than inserting it as markup. At roughly 58 average reads/s, this implementation can be perfectly reasonable. SQL can store text; “billions of records” is not sufficient evidence to reject a relational engine without considering time horizon, partitioning and operational requirements.

Expiry, backups and scope of the baseline

architecture · baselineA paste in one database commit

The initial service stores text and publication metadata together, so commit directly establishes readability.

A paste in one database commitThe initial service stores text and publication metadata together, so commit directly establishes readability. client to app: 1. Create / authorized read; app to db: 2. Commit paste and request result; db to app: 3. Read committed text and grant; app to client: 4. Return exact saved text1. Create / authorized read2. Commit paste and requestresult3. Read committed text andgrant4. Return exact saved textACTORAuthor and readerclientsSERVICEPaste applicationSTORESQL text, metadataand grantssync
Read each connection in order
  1. sync1. Create / authorized readAuthor and reader clients → Paste application
  2. sync2. Commit paste and request resultPaste application → SQL text, metadata and grants
  3. sync3. Read committed text and grantSQL text, metadata and grants → Paste application
  4. sync4. Return exact saved textPaste application → Author and reader clients

08Find the baseline flaws

Bottleneck / counterexample Evidence and design consequence
Retained bytes and slow transfers After years of retention the database carries tens of terabytes of text alongside small indexed rows. Backups, replication traffic and cache working sets now include content that rarely changes. A 10 MB paste can evict many frequently accessed metadata pages. Slow uploads also occupy application connections; 580 concurrent five-second uploads can starve reads if both share a small worker pool. Increasing metadata indexes cannot fix byte-transfer contention.
Unsafe two-store publication Suppose the API saves READY metadata before uploading the object. A crash between those writes exposes a paste with no bytes. Uploading first has a safer failure: if the metadata transaction fails, the object remains hidden and can be recovered or removed. Two independent stores cannot prevent that leftover object without additional coordination. Choose hidden unfinished work over a published broken paste.
Cleanup racing publication Cleanup can reintroduce the first bug. A sweeper observes a long-running upload as old, deletes the object, and a late uploader marks the row ready. A grace period alone is not a proof unless upload lifetime is bounded and publication checks that bound atomically. Both operations must check and change the same saved metadata state so publication and cleanup cannot both win.

09Improve the design, step by step

  1. Separate immutable bodies from metadata. Retained text and backup pressure trigger object storage. Reserve UPLOADING metadata, stream to a generation-specific object, verify it, then transition to READY. This keeps database queries small and body scaling independent. It costs extra requests, reconciliation and a two-store failure protocol. Keeping text in SQL remains the better alternative at small volume when one commit is more valuable than storage separation.

  2. Separate transfer and read capacity. Slow uploads trigger dedicated upload pools with bounded streaming buffers; read APIs have independent concurrency limits. Larger future objects could use short-lived direct upload authorization, but the 10 MB limit does not automatically justify multipart orchestration. The improvement is read latency under slow senders. The cost is another capacity pool and partial-upload cleanup. A single event-driven pool is simpler if load testing proves adequate isolation.

  3. Add public-body caching and replicated metadata. A viral log and zone failures trigger this step. Replicas protect accepted metadata; cached immutable bodies reduce origin work. Authorization still precedes private delivery, and visibility checks bound public deletion delay. Costs include RAM, eviction decisions and invalidation/freshness handling; caching everything wastes memory on one-read pastes. Retain small frequently reused bodies and separately cap very large entries.

  4. Add durable cleanup and delayed counters. Expiry scans and synchronous view-count contention trigger background work. Deletion writes a cleanup outbox entry in the same metadata transaction; a relay and worker retry external deletion. Approximate view events go to a bounded queue. The improvement is predictable reads and eventual reclamation. Costs are queue retention, duplication and lag monitoring. Exact view accounting is rejected unless it becomes a billing requirement; in that case change the acknowledgement and event durability contract explicitly.

Logical metadata partitions become a subsequent step only after measured storage or write limits, not a required starting box. Hashing IDs spreads distinct pastes; replicated body caches address one hot paste. Health-aware balancing sends traffic only to ready service instances.

10Detailed architecture

Separate transfer and read pools

The final system has an authenticated upload API and an independently scaled read API behind an edge router. Both check the replicated metadata database. Upload workers stream to a private immutable object store; they cannot grant visibility merely by finishing an object write. Read workers enforce metadata policy, then retrieve bytes through an internal body cache. A public content-delivery layer can reduce geographic transfer latency under the explicit freshness contract, while private requests remain authorization-gated.

Metadata and byte-store authority

The metadata leader owns paste states, grants, request identities and cleanup intentions. Its replicas provide the chosen failure tolerance. The object store has its own replication configuration, repair and backup obligations; duplicating metadata does not protect missing text. A reconciler inspects expired upload leases and resumes or cancels them through metadata transitions. A cleanup relay consumes committed outbox rows and queues object removal. Counters are a separate derived store.

Synchronous boundary and overload control

Synchronous creation ends only when the READY transition commits; synchronous reading ends only after authorized bytes are selected and streamed. Content statistics and physical reclamation may lag. The private storage boundary is important: an internal cache hit or guessed object key cannot skip authorization. If direct download URLs are introduced later, the time during which anyone holding that URL can download without another permission check becomes an explicit revocation limitation. Any such period conflicts with the immediate private-read revocation contract; retain authorization at the delivery edge or explicitly weaken that contract before introducing such URLs.

architecture · finalMetadata gates immutable body delivery

Object existence does not publish a paste. The metadata owner grants READY and arbitrates publication against garbage collection.

Metadata gates immutable body deliveryObject existence does not publish a paste. The metadata owner grants READY and arbitrates publication against garbage collection. client to edge: 1. Upload or read paste; edge to upload: 2a. Admit bounded upload; edge to read: 2b. Route read; upload to meta: 3. Reserve / guarded READY; upload to objects: 4. Stream and verify g1; meta to replicas: 5. Replicate committed state; read to meta: 6. Check state, expiry and grant; read to cache: 7. Fetch authorized body; cache to objects: 8. Miss: fetch immutable g1; read to client: 9. Stream escaped/raw text; gc to meta: 10. Guard GC_PENDING / read outbox; gc to queue: 11. Queue exact generation cleanup; queue to gc: 12. Retry cleanup delivery; gc to objects: 13. Delete terminal generation; read to queue: 14. Best-effort view event; queue to counter: 15. Aggregate views; counter to stats: 16. Update derived totals1. Upload or read paste2a. Admit bounded upload2b. Route read3. Reserve / guarded READY4. Stream and verify g15. Replicate committed state6. Check state, expiry andgrant7. Fetch authorized body8. Miss: fetch immutable g19. Stream escaped/raw text10. Guard GC_PENDING / readoutbox11. Queue exact generationcleanup12. Retry cleanup delivery13. Delete terminal generation14. Best-effort view event15. Aggregate views16. Update derived totalsACTORWeb and API clientsSERVICETLS routing and bytelimitsG1SERVICEAuthenticated uploadAPIG1SERVICEAuthorized read APIG1STOREPaste metadataauthorityG2STOREMetadata replicasG2STOREPrivate immutablebody storeG2CACHEInternal body cacheG2WORKERLease reconciler andcleanup relayG3QUEUECleanup and viewqueuesG3WORKERView counter workersG3STOREApproximatestatisticsG3syncreplicationasyncG1 Request and authorization boundaryG2 Publication and byte durabilityG3 Recoverable background work
Read each connection in order
  1. sync1. Upload or read pasteWeb and API clients → TLS routing and byte limits
  2. sync2a. Admit bounded uploadTLS routing and byte limits → Authenticated upload API
  3. sync2b. Route readTLS routing and byte limits → Authorized read API
  4. sync3. Reserve / guarded READYAuthenticated upload API → Paste metadata authority
  5. sync4. Stream and verify g1Authenticated upload API → Private immutable body store
  6. replication5. Replicate committed statePaste metadata authority → Metadata replicas
  7. sync6. Check state, expiry and grantAuthorized read API → Paste metadata authority
  8. sync7. Fetch authorized bodyAuthorized read API → Internal body cache
  9. sync8. Miss: fetch immutable g1Internal body cache → Private immutable body store
  10. sync9. Stream escaped/raw textAuthorized read API → Web and API clients
  11. sync10. Guard GC_PENDING / read outboxLease reconciler and cleanup relay → Paste metadata authority
  12. async11. Queue exact generation cleanupLease reconciler and cleanup relay → Cleanup and view queues
  13. async12. Retry cleanup deliveryCleanup and view queues → Lease reconciler and cleanup relay
  14. async13. Delete terminal generationLease reconciler and cleanup relay → Private immutable body store
  15. async14. Best-effort view eventAuthorized read API → Cleanup and view queues
  16. async15. Aggregate viewsCleanup and view queues → View counter workers
  17. async16. Update derived totalsView counter workers → Approximate statistics

11Write path and acknowledgement

Uploading saves the bytes; committing READY metadata permits readers to retrieve them. Request paste-create-88 reserves paste p7Hk2Lm9; each step below states what a retry or collector can safely observe.

  1. Authenticate the owner, validate byte quota and text encoding, and calculate a payload identity. Reserve p7Hk2Lm9 with generation g1 and UPLOADING, together with its request key in a local transaction.
  2. Stream bytes to pastes/p7Hk2Lm9/g1/body using a bounded buffer. Retry a failed transfer to the same immutable generation only with matching expected content; never reuse that identity for changed text.
  3. Verify storage acknowledgement, length and checksum. This confirms the body exists but does not yet make the paste public.
  4. Begin a metadata transaction, lock p7Hk2Lm9, and require state UPLOADING, matching g1, and a still-valid publication lease. Atomically set READY and save the successful request result.
  5. Commit, then return 201. If the response disappears, the next request with paste-create-88 returns the committed result. If the lease expired, return a recoverable pending/failed state rather than bypassing the guard.
  6. A timed-out transfer with unknown outcome is inspected by object identity and checksum. Existing correct bytes may be reused; absent or mismatched bytes are retried or rejected without exposing them.
  7. If the owner abandons the operation, the reconciler eventually transitions it to GC_PENDING and reclaims g1. A late uploader cannot publish that generation after the transition.

No remote transaction spans object storage and SQL. The ordering plus metadata guards ensure a crash produces invisible recoverable work, rather than an acknowledged paste with missing content.

Make immutable object creation an enforced write rule. For an S3 implementation, a conditional create such as If-None-Match: * prevents overwriting an existing generation; on an existing-object result, verify the saved content identity before reusing it. Keep cleanup rights separate from upload rights. The product’s zone-loss promise also requires a storage class with the corresponding multi-zone durability, not a single-zone option chosen only for latency.

12Read and delivery path

Private reads must authorize against current metadata before exposing even a cached body. The example uses paste p7Hk2Lm9, reader u31 and immutable object generation g1.

  1. The reader sends an authenticated GET for p7Hk2Lm9. The read API obtains the authoritative metadata and current grant decision. In this example Grant(p7Hk2Lm9,u31) exists.
  2. It requires READY, an unexpired deadline, no deletion marker and allowed identity. Failure stops before body-cache lookup results are exposed. A storage outage is a 503; a denied or unavailable paste follows the chosen nondisclosure response.
  3. The API requests immutable object generation g1 through the internal cache. A miss reads private object storage; simultaneous misses can share one refill. A cache entry becomes valid only after its complete length and checksum are verified. A streaming response uses storage integrity checks and may provide an end-to-end checksum; if verification fails after transmission starts, terminate the response and report failure rather than claim a complete successful body. Do not pretend a whole-body checksum can validate the first byte before the rest has arrived.
  4. Raw delivery sets the plain-text type and disables content sniffing. The browser page escapes HTML metacharacters. The owner's pasted <script> remains text.
  5. The API streams with backpressure: if the reader reads slowly, it does not buffer the whole object repeatedly. It enforces a per-connection and account byte budget.
  6. A bounded view event is emitted asynchronously. Repeated reads can increase approximate counts; they do not mutate the authoritative paste row on every view.

A public edge copy carries an absolute visibility deadline anchored to authoritative validation, bounded by both the 30-second policy and the paste expiry. A delayed fill cannot start a new 30-second lifetime; use a conservative time margin for uncertainty. Keep browser responses no-store for the stated deletion behavior, while controlled internal/edge caches enforce those deadlines. A private body may stay cached internally longer because a fresh permission check gates each request.

13Correctness deep dive

Publication and collection share one authority

Uploader U may try to publish while collector G removes an abandoned upload. Both must check and change the same metadata row; finding an object in storage does not establish permission to publish or delete it. Require every upload to have a finite lease, and never permit an expired generation to publish without a new coordinated reservation.

Transition Atomic precondition at metadata owner Durable effect
Reserve Request identity absent and alias unclaimed UPLOADING(g1, deadline D) plus request row
Publish UPLOADING(g1), now < D, verified object identity READY(g1) and successful request result
Collect abandoned upload UPLOADING(g1), now ≥ D GC_PENDING(g1) plus cleanup outbox
Owner delete Authorized READY(g1) DELETED(g1) plus cleanup outbox
Retry cleanup Matching terminal generation Repeat object delete; record completion

Which state transition wins?

Crash recovery and retained payloads

A crash after READY but before HTTP response is recovered by the request result. A crash after GC_PENDING but before external deletion is recovered by the durable outbox. The relay may publish cleanup twice; deletion by exact immutable generation is harmless when repeated. If U continues uploading after collection, its late bytes remain invisible and a subsequent sweep removes them. Bound upload credentials and maximum transfer duration to prevent indefinite orphan recreation. The proof depends on enforced state guards, not on hoping a grace interval exceeds every slow request.

sequence · cleanup-raceCollector wins before a late uploader publishes

Both actors must change the same metadata row. Once GC_PENDING commits, no late upload may expose generation g1.

Collector wins before a late uploader publishesBoth actors must change the same metadata row. Once GC_PENDING commits, no late upload may expose generation g1. up to meta: Reserve UPLOADING g1 until D; up to obj: Upload immutable g1; gc to meta: After D: lock and check UPLOADING; meta to gc: Commit GC_PENDING and outbox; up to meta: Attempt READY for g1; meta to up: Reject: state no longer UPLOADING; gc to obj: Delete terminal generation g1; gc to meta: Record cleanup completionPARTICIPANTUploaderPARTICIPANTObject storePARTICIPANTMetadata ownerPARTICIPANTCollector1. Reserve UPLOADING g1 until D2. Upload immutable g13. After D: lock and checkUPLOADING4. Commit GC_PENDING andoutbox5. Attempt READY for g16. Reject: state no longer UPLOADING7. Delete terminal generation g18. Record cleanup completionsyncreturn
Read each connection in order
  1. syncReserve UPLOADING g1 until DUploader → Metadata owner
  2. syncUpload immutable g1Uploader → Object store
  3. syncAfter D: lock and check UPLOADINGCollector → Metadata owner
  4. returnCommit GC_PENDING and outboxMetadata owner → Collector
  5. syncAttempt READY for g1Uploader → Metadata owner
  6. returnReject: state no longer UPLOADINGMetadata owner → Uploader
  7. syncDelete terminal generation g1Collector → Object store
  8. syncRecord cleanup completionCollector → Metadata owner

14Failure and recovery

Failure / trigger User outcome, surviving state and recovery
Object succeeds, metadata commit fails the owner receives no 201. The row remains UPLOADING and the body may exist. A retry before its lease deadline verifies g1 and attempts publication. After collection wins, the client gets a failed/expired attempt and may intentionally create a new request. The reader never sees a ready pointer to this uncommitted body.
Metadata authority is partitioned The majority can continue if available; a minority does not authorize private reads or publish new pastes. Public cached reads may continue only within their pre-issued bounded lifetime. When it expires, return unavailable. A stale grant cache cannot be treated as a valid current permission decision merely to improve availability. Surviving object bytes remain intact while metadata service recovers.
A viral paste and object-store throttling coincide Coalesce cache misses, limit origin concurrency and protect metadata requests from body-transfer queues. Prefer 503 with retry guidance over unlimited waiting that exhausts sockets. Maximum-size bodies have separate admission limits; otherwise a few large responses can starve many tiny incident logs. Warm popular content gradually after cache loss.

Backup and recovery boundaries

Replicas protect node/zone failures, while backups protect accidental deletion and corruption according to tested recovery points. Restore metadata and body generations together and check every sampled READY reference. A residual regional-disaster loss window remains unless synchronous regional durability is added; naming an object-store product does not eliminate that tradeoff.

15Operations, security, and cost

Integrity, queue and latency signals

Alert on any READY row whose referenced object is absent, the oldest UPLOADING lease, orphan bytes, cleanup-outbox lag and expired content still served. Separate latency for metadata, first byte and whole body. Track cache byte-hit ratio as well as request-hit ratio: a cache serving many tiny pastes may still leave large origin egress. Count authorization denials and request bytes independently to detect enumeration and upload abuse.

Metadata and body-cache cost

For one million daily creations, storing a 200-byte cleanup/request bookkeeping row per paste adds about 200 MB/day before indexes. Ten years would add roughly 730 GB of such raw metadata if never compacted. Define retention for completed request keys and compact permanent alias claims rather than accidentally keeping every transient log forever. The dominant cost remains retained bytes and copies, followed by object operations and delivery. Compression may reduce text bytes, but enforce decompressed size limits and checksum the canonical intended representation.

Dual-read storage migration

Roll out object storage behind a dual-read migration: write new pastes to the new scheme, copy older bodies, verify lengths/checksums, switch a row's pointer transactionally, then remove the old database payload after a rollback window. Do not delete the old copy merely because a copy job started. Test crash points, expired upload credentials, a collector/uploader interleaving, denied private-cache hits and backup restoration.

Safe rendering and privacy

Authentication, rate limits and safe rendering are required before public launch. Avoid recording secret paste contents in request logs or analytics. Deduplication, if added, must not reveal whether another tenant owns matching text; shared object reclamation then requires transactional references or a verified reachability process before deletion.

Restoring permanent alias claims

Restore permanent alias claims as well as visible pastes. If a regional recovery point loses some recent claims, freeze new allocations in the old namespace until claims are recovered; a fresh namespace can host new random pastes without giving an old incident URL a new owner. Also do not restore old private grants as current authority without reconciling revocations that may have occurred after the backup.

16Decision ledger and limitations

Keeping text in SQL and moving it to object storage are both valid choices. The deciding issue is whether lower byte-storage and backup pressure justify coordinating publication across two stores. The table makes the consequences of that split explicit alongside the access and counting policies.

Chosen mechanism Benefit Cost / remaining limit Reconsider when
Immutable paste content Simple caches and stable checksums Editing creates a new identity Collaborative editing becomes a requirement
Bytes before READY metadata No published pointer to a missing upload Orphans and reconciliation A single transactional blob store is simpler at small scale
Generation guard for cleanup Late uploader cannot publish collected bytes Finite leases and retry UX Extremely long uploads need renewable guarded sessions
Current private authorization Revoked readers cannot begin new authorized reads Metadata outage reduces private-read availability Product accepts a bounded authorization cache
Approximate asynchronous counts No hot counter on the read path Delayed or missing events Counts become billing/audit evidence

Six characters from a 64-symbol alphabet offer about 68.7 billion combinations, but a finite namespace does not make random draws unique or make guessing impossible. We choose a larger generated namespace, enforce uniqueness at insertion and rely on identity-based grants for private content. A preallocated key-generation service could reserve batches durably; it adds failover coordination and wastes uncertain unused batches on crashes. At 116 peak creates/s it is unnecessary.

We do not promise that revocation deletes copies the reader already saved, or that a public paste remains confidential because its URL is obscure. We also do not claim a SQL database must be replaced by a key-value store at a specific row count. The justified boundary is small indexed metadata versus independently retained immutable bytes.

17Interview closing

“I am building an immutable text-sharing service: the owner publishes one paste and the reader receives its exact authorized bytes. The workload is around twelve creates and fifty-eight reads per second on average, but ten-year content retention reaches 36.5 TB before copies. I would start with text and metadata in one transaction, then separate bodies when storage and backup pressure justify the complexity.

“The final design reserves an upload generation, writes and verifies the object, and only then commits READY metadata. Private reads authorize before delivery. A metadata state transition arbitrates publication against cleanup, so a collector cannot remove bytes that a late uploader is still allowed to publish. Lost responses recover through the same request identity. Public caching and delayed statistics improve read cost; they do not become the authority for existence or permission.

“I accept temporary unavailability when current private authorization cannot be established. My next measurements are how many large-paste transfers overlap during peak traffic, whether every READY record points to complete content, and how long unused upload objects wait for deletion.”

If the interviewer adds editable pastes, preserve immutable body versions and atomically switch a metadata version pointer with an expected-version precondition. Explain whether readers get the latest version or a stable historical link. Do not overwrite the old object under a cacheable key and assume every cache changes simultaneously.

Practise the interview questions

Say your answer aloud before opening the model answer. Then answer the follow-up and compare the reasoning.

Foundation · Question 1

How is Pastebin different from a URL shortener?

Reveal a model answer

“We own the text bytes. A redirect service returns another address, while this service must retain and deliver the original paste. That makes upload limits, content durability, rendering safety, and deletion of actual bytes part of the design.”

What the answer must demonstrate: Do not start with two stores without explaining their cost.

Applied · Question 2

The object exists but the ready transaction failed. What does the reader see?

Reveal a model answer

“The reader cannot see an uploading paste. The create retry verifies the existing object and resumes the state transition; a lease-based reconciler eventually cleans abandoned work. I prefer an invisible orphan over a visible broken paste.”

What the answer must demonstrate: An object upload is not an atomic metadata commit.

Foundation · Question 3

Does the expiry worker enforce the expiration deadline?

Reveal a model answer

“The read path enforces the deadline. The worker reclaims storage later. Otherwise an overloaded worker would silently extend every expired paste’s public lifetime.”

What the answer must demonstrate: Do not treat physical cleanup as authorization.

Applied · Question 4

One customer repeatedly reads a 10 MB paste. Is a cache always helpful?

Reveal a model answer

“It may reduce origin bandwidth, but that object can displace thousands of small pastes. I budget cache bytes, use size-aware admission, and measure byte-hit ratio as well as request-hit ratio. A CDN may be better for large public immutable content.”

What the answer must demonstrate: Maximum size and average size drive different limits.

Follow-up · Question 5

How do you delete a paste across database and object store?

Reveal a model answer

“Commit a deleted state and cleanup outbox event first, invalidate serving paths, and remove bytes asynchronously. Retried deletion is harmless. If another retained record shares the object, cleanup must preserve it.”

What the answer must demonstrate: A cleanup failure must not undo logical deletion.

Follow-up · Question 6

Object storage is unavailable. Should the API return 404?

Reveal a model answer

“No. The metadata says the paste exists, so missing access to storage is an availability failure. Serve a valid authorized cache copy or return a retryable error; do not mislead clients into treating retained content as permanently missing.”

What the answer must demonstrate: Differentiate absent content from an unreachable dependency.

Applied · Question 7

A collector deletes a slow upload just before its API publishes it. How do you prevent a broken paste?

Reveal a model answer

Publication and cleanup must check and change the same metadata row. Publication requires UPLOADING with the correct generation and an unexpired lease. Collection atomically changes an expired UPLOADING row to GC_PENDING before deleting its exact object. If collection wins, publication fails; if publication wins READY, collection cannot use a stale observation to delete it.

What the answer must demonstrate: Require the collector to recheck authority rather than trust an old scan.

Follow-up · Question 8

Can the private-paste cache skip the database because text is immutable?

Reveal a model answer

No. The cached bytes remain identical, but authorization can change. The reader must pass current grant and state checks before the cache response is exposed. I can cache bytes internally while keeping access decisions authoritative.

What the answer must demonstrate: Separate content immutability from permission freshness.

Blank-page exercise · 45 minutes

Build the answer yourself

Design an immutable text-sharing service with a 10 MB limit and public, unlisted and private pastes. Compare single-database publication with separate byte storage, then prove recovery after upload succeeds but metadata publication fails.

  • Define public, unlisted, and private access.
  • Compute content bytes separately from metadata and QPS.
  • Show uploading→ready with an actual object key.
  • Replay the upload/metadata failure without duplicate publication.
  • Explain cache expiry and owner deletion.

Check that each component and design decision follows from your requirements and workload.

Recall the key ideas

Answer from memory before opening each card. Explain why the choice works and what it costs. Revisit missed cards tomorrow.

Design PastebinWhen may a paste creation response report READY?Recall first, then reveal

After content is stored and metadata commits ready. An uploaded object alone is not published content.

Bytes first; ready second.

Return to lesson
Design PastebinAn object exists but its metadata is uploading. What is that?Recall first, then reveal

Recoverable partial work. Retry or reconcile using its request identity and upload lease.

Partial work needs durable state.

Return to lesson
Design PastebinDoes an unlisted paste enforce team membership?Recall first, then reveal

No. A private paste needs authentication and an explicit read grant, including when caching is used.

Possession is not membership.

Return to lesson

Final revision

Summary and interview notes

Pastebin owns immutable content bytes as well as their public identity. Start with one database transaction, then split bytes from metadata only when storage and transfer costs justify the publication, authorization, and cleanup protocol that follows.

Remember these points

  • One million 10 KB pastes per day produce 10 GB/day and 36.5 TB over ten years; maximum-size concurrency requires a separate memory and bandwidth estimate.
  • READY is a metadata commit after verified immutable bytes exist, not merely a completed upload.
  • Publication and cleanup check the same upload generation and state. If cleanup claims it first, the uploader cannot mark its deleted bytes READY.
  • Private cached bytes still require current authorization; a download URL that remains usable without a fresh permission check weakens immediate revocation.
  • Public cache deadlines must remain anchored to validation, and cleanup lag must never extend logical expiry.

Interview tips

  • Draw the baseline’s single commit boundary before showing the two-store design.
  • Interleave a collector with a late uploader and identify the winning metadata transition.
  • Separate first-byte latency, full-body transfer time, and proof that the completed body matches its checksum.

Important qualifications

  • S3 read-after-write consistency does not make a transaction with the metadata database.
  • A regional restore needs alias and grant history, not only visible text objects.
  • Whole-body checksum failure may be discovered after streaming starts; terminate and treat the transfer as failed.

Technical references

Practice marks stay in this browser.