System-design interview · Core interviews
Design Pastebin
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 practiceUseful 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.
Dotted concept links open the relevant explanation in a new tab.
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
- Create with request key: One paste identity for repeated attempts of the same payload.
- Choose custom alias: Claim if unused; conflict rather than overwrite another paste.
- Read public/unlisted: No login required; visibility still obeys expiry and deletion.
- Read private: Authenticated current grant is required before bytes are delivered.
- Delete: Owner revokes new reads; physical bytes may be reclaimed later.
- 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
- 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.
- Availability: 99.9% eligible read availability.
- Durability: Acknowledge creation only after configured replicated storage accepts it. Accepted content survives one storage-node or availability-zone failure.
- Regional recovery: Use tested backups with an initial one-hour restore objective and an explicitly measured backup recovery point.
- Private authorization: Check current authoritative metadata on every new request. A completed grant revocation blocks requests begun afterward; already delivered bytes cannot be recalled.
- 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.
- 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
The initial service stores text and publication metadata together, so commit directly establishes readability.
Read each connection in order
- sync1. Create / authorized readAuthor and reader clients → Paste application
- sync2. Commit paste and request resultPaste application → SQL text, metadata and grants
- sync3. Read committed text and grantSQL text, metadata and grants → Paste application
- 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
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.
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.
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.
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.
Object existence does not publish a paste. The metadata owner grants READY and arbitrates publication against garbage collection.
Read each connection in order
- sync1. Upload or read pasteWeb and API clients → TLS routing and byte limits
- sync2a. Admit bounded uploadTLS routing and byte limits → Authenticated upload API
- sync2b. Route readTLS routing and byte limits → Authorized read API
- sync3. Reserve / guarded READYAuthenticated upload API → Paste metadata authority
- sync4. Stream and verify g1Authenticated upload API → Private immutable body store
- replication5. Replicate committed statePaste metadata authority → Metadata replicas
- sync6. Check state, expiry and grantAuthorized read API → Paste metadata authority
- sync7. Fetch authorized bodyAuthorized read API → Internal body cache
- sync8. Miss: fetch immutable g1Internal body cache → Private immutable body store
- sync9. Stream escaped/raw textAuthorized read API → Web and API clients
- sync10. Guard GC_PENDING / read outboxLease reconciler and cleanup relay → Paste metadata authority
- async11. Queue exact generation cleanupLease reconciler and cleanup relay → Cleanup and view queues
- async12. Retry cleanup deliveryCleanup and view queues → Lease reconciler and cleanup relay
- async13. Delete terminal generationLease reconciler and cleanup relay → Private immutable body store
- async14. Best-effort view eventAuthorized read API → Cleanup and view queues
- async15. Aggregate viewsCleanup and view queues → View counter workers
- 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.
- 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. - Stream bytes to
pastes/p7Hk2Lm9/g1/bodyusing a bounded buffer. Retry a failed transfer to the same immutable generation only with matching expected content; never reuse that identity for changed text. - Verify storage acknowledgement, length and checksum. This confirms the body exists but does not yet make the paste public.
- 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.
- 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.
- 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.
- 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.
- 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. - 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.
- 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.
- Raw delivery sets the plain-text type and disables content sniffing. The browser page escapes HTML metacharacters. The owner's pasted
<script>remains text. - 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.
- 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.
Both actors must change the same metadata row. Once GC_PENDING commits, no late upload may expose generation g1.
Read each connection in order
- syncReserve UPLOADING g1 until DUploader → Metadata owner
- syncUpload immutable g1Uploader → Object store
- syncAfter D: lock and check UPLOADINGCollector → Metadata owner
- returnCommit GC_PENDING and outboxMetadata owner → Collector
- syncAttempt READY for g1Uploader → Metadata owner
- returnReject: state no longer UPLOADINGMetadata owner → Uploader
- syncDelete terminal generation g1Collector → Object store
- 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.
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.”
Interviewer follow-up
Could the first version use only SQL?
Reveal the follow-up answer
“Yes. For bounded small text and modest traffic, storing the body in the same row simplifies atomic creation. I introduce object storage when measured byte volume or independent serving needs justify it.”
What the answer must demonstrate: Do not start with two stores without explaining their cost.
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.”
Interviewer follow-up
How do you avoid creating a second object on retry?
Reveal the follow-up answer
“Use the same reserved paste ID and immutable object key, verify expected content, and persist the request result. A changed payload with the same request key is rejected.”
What the answer must demonstrate: An object upload is not an atomic metadata commit.
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.”
Interviewer follow-up
What about cached text?
Reveal the follow-up answer
For a public paste, retain an absolute visibility deadline from the authoritative check, bounded by the paste’s expiry, and do not restart its lifetime on delayed refill. For private text, current authorization still gates every request even when the bytes are cached. Cleanup and best-effort invalidation alone cannot establish either promise.
What the answer must demonstrate: Do not treat physical cleanup as authorization.
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.”
Interviewer follow-up
What protects API memory during upload?
Reveal the follow-up answer
“Stream with a strict size limit and bounded concurrency. Do not buffer every permitted 10 MB body before rejecting overload.”
What the answer must demonstrate: Maximum size and average size drive different limits.
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.”
Interviewer follow-up
What if object deletion fails?
Reveal the follow-up answer
“Visibility remains revoked; the durable cleanup task retries and we alert on age. Storage reclamation can lag without reopening access.”
What the answer must demonstrate: A cleanup failure must not undo logical deletion.
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.”
Interviewer follow-up
What should you monitor?
Reveal the follow-up answer
“Separate metadata lookup errors, missing-object integrity errors, and backend timeouts. Their recovery actions differ.”
What the answer must demonstrate: Differentiate absent content from an unreachable dependency.
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.
Interviewer follow-up
Would a long grace period alone be sufficient?
Reveal the follow-up answer
Only if every producer is provably unable to publish after that period. I prefer the explicit state guard because real networks and suspended processes can outlive guessed grace periods.
What the answer must demonstrate: Require the collector to recheck authority rather than trust an old scan.
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.
Interviewer follow-up
What changes if you issue a direct object download URL?
Reveal the follow-up answer
A direct URL is a bearer capability for its lifetime unless the delivery layer rechecks permission. For our immediate private-read revocation promise, a positive expiry interval is still too long. I would retain authorization at the serving edge, or explicitly agree to a weaker bounded-revocation contract before exposing direct download URLs.
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 lessonDesign 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 lessonDesign 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 lessonFinal 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
- Amazon S3 data consistency modelConfirms object read-after-write behavior; it does not provide a transaction with a separate metadata database.
- Transactional outbox patternSupports the reliable handoff from a committed deletion to asynchronous cleanup.
- Amazon S3: Conditional WritesVerified conditional creation prevents overwriting an existing generation; metadata publication remains a separate transaction.
- Amazon S3: Checking Object IntegrityVerified explicit checksums rather than assuming every ETag is a whole-object hash.
Practice marks stay in this browser.